[libcxx-commits] [libcxx] [libc++] Implement the `indirect` half of P3019R14: Vocabulary Types for Composite Class Design (PR #166717)
Victor Chernyakin via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Nov 6 13:03:54 PST 2025
https://github.com/localspook updated https://github.com/llvm/llvm-project/pull/166717
>From 0cf68e842293a96db80d9859926c1e664ce53f2c Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 05:17:48 +0000
Subject: [PATCH 1/8] [libc++] Implement the `indirect` half of P3019R11:
Vocabulary Types for Composite Class Design
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__memory/indirect.h | 332 ++++++++++++++++++
libcxx/include/memory | 90 +++++
libcxx/include/module.modulemap.in | 1 +
libcxx/include/version | 2 +
libcxx/modules/std/memory.inc | 9 +
.../indirect.obs/assert.arrow.pass.cpp | 35 ++
.../indirect.obs/assert.deref.pass.cpp | 38 ++
.../indirect.obs/deref.nodiscard.verify.cpp | 25 ++
.../get_allocator.nodiscard.verify.cpp | 19 +
.../valueless_after_move.nodiscard.verify.cpp | 19 +
.../indirect.swap/assert.swap.pass.cpp | 32 ++
.../indirect/no_specializations.verify.cpp | 23 ++
.../memory.version.compile.pass.cpp | 27 ++
.../version.version.compile.pass.cpp | 27 ++
.../indirect/indirect.assign/copy.pass.cpp | 125 +++++++
.../indirect/indirect.assign/move.pass.cpp | 167 +++++++++
.../perfect_forwarding.pass.cpp | 107 ++++++
.../indirect/indirect.comp.with.t/eq.pass.cpp | 76 ++++
.../indirect.comp.with.t/three_way.pass.cpp | 72 ++++
.../indirect/indirect.ctor/copy.pass.cpp | 117 ++++++
.../indirect/indirect.ctor/default.pass.cpp | 91 +++++
.../indirect.ctor/in_place_t.pass.cpp | 125 +++++++
.../indirect/indirect.ctor/init_list.pass.cpp | 113 ++++++
.../indirect/indirect.ctor/move.pass.cpp | 112 ++++++
.../indirect.ctor/perfect_forwarding.pass.cpp | 104 ++++++
.../indirect_array.verify.cpp | 24 ++
.../indirect_cv_qualified.verify.cpp | 26 ++
.../indirect_in_place_t.verify.cpp | 21 ++
.../indirect_in_place_type_t.verify.cpp | 21 ++
.../indirect_nonobject.verify.cpp | 24 ++
.../indirect/indirect.hash/hash.pass.cpp | 47 +++
.../indirect/indirect.obs/arrow.pass.cpp | 43 +++
.../indirect/indirect.obs/deref.pass.cpp | 54 +++
.../indirect.obs/get_allocator.pass.cpp | 38 ++
.../valueless_after_move.pass.cpp | 38 ++
.../indirect/indirect.relops/eq.pass.cpp | 86 +++++
.../indirect.relops/three_way.pass.cpp | 82 +++++
.../indirect/indirect.swap/swap.pass.cpp | 103 ++++++
libcxx/test/support/test_allocator.h | 14 +-
libcxx/test/support/test_convertible.h | 8 +
.../generate_feature_test_macro_components.py | 5 +
44 files changed, 2520 insertions(+), 7 deletions(-)
create mode 100644 libcxx/include/__memory/indirect.h
create mode 100644 libcxx/test/libcxx/memory/indirect/indirect.obs/assert.arrow.pass.cpp
create mode 100644 libcxx/test/libcxx/memory/indirect/indirect.obs/assert.deref.pass.cpp
create mode 100644 libcxx/test/libcxx/memory/indirect/indirect.obs/deref.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/memory/indirect/indirect.obs/get_allocator.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/memory/indirect/indirect.obs/valueless_after_move.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/memory/indirect/indirect.swap/assert.swap.pass.cpp
create mode 100644 libcxx/test/libcxx/memory/indirect/no_specializations.verify.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.assign/copy.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.assign/move.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.assign/perfect_forwarding.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/eq.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/three_way.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.ctor/copy.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.ctor/default.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.ctor/in_place_t.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.ctor/init_list.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.ctor/perfect_forwarding.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_array.verify.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_cv_qualified.verify.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_t.verify.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_type_t.verify.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_nonobject.verify.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.hash/hash.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.obs/arrow.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.relops/eq.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.relops/three_way.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/indirect/indirect.swap/swap.pass.cpp
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d5ed9188b1b23..72ad135f61456 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -472,6 +472,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_hazard_pointer`` *unimplemented*
---------------------------------------------------------- -----------------
+ ``__cpp_lib_indirect`` ``202502L``
+ ---------------------------------------------------------- -----------------
``__cpp_lib_inplace_vector`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_is_sufficiently_aligned`` ``202411L``
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index e0e47b864d38f..590853b07c096 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -115,7 +115,7 @@
"`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>`__",""
"`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>`__",""
+"`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","|Partial|","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__","``polymorphic`` is not yet implemented."
"","","","","","",""
"`P2996R13 <https://wg21.link/P2996R13>`__","Reflection for C++26","2025-06 (Sofia)","","","`#148123 <https://github.com/llvm/llvm-project/issues/148123>`__",""
"`P3394R4 <https://wg21.link/P3394R4>`__","Annotations for Reflection","2025-06 (Sofia)","","","`#148124 <https://github.com/llvm/llvm-project/issues/148124>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 57032ce26d4fd..37587c5d56c31 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -582,6 +582,7 @@ set(files
__memory/construct_at.h
__memory/destroy.h
__memory/destruct_n.h
+ __memory/indirect.h
__memory/inout_ptr.h
__memory/is_sufficiently_aligned.h
__memory/noexcept_move_assign_container.h
diff --git a/libcxx/include/__memory/indirect.h b/libcxx/include/__memory/indirect.h
new file mode 100644
index 0000000000000..be55d2d0f18ef
--- /dev/null
+++ b/libcxx/include/__memory/indirect.h
@@ -0,0 +1,332 @@
+// -*- 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___MEMORY_INDIRECT_H
+#define _LIBCPP___MEMORY_INDIRECT_H
+
+#include <__config>
+
+#include <__compare/strong_order.h>
+#include <__compare/synth_three_way.h>
+#include <__functional/hash.h>
+#include <__fwd/memory_resource.h>
+#include <__memory/addressof.h>
+#include <__memory/allocation_guard.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/swap_allocator.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_cv.h>
+#include <__utility/exchange.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <initializer_list>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, class _Allocator = allocator<_Tp>>
+class _LIBCPP_NO_SPECIALIZATIONS indirect {
+public:
+ using value_type = _Tp;
+ using allocator_type = _Allocator;
+ using pointer = allocator_traits<_Allocator>::pointer;
+ using const_pointer = allocator_traits<_Allocator>::const_pointer;
+
+ static_assert(__check_valid_allocator<allocator_type>::value);
+ static_assert(is_same_v<typename allocator_type::value_type, value_type>);
+ static_assert(is_object_v<value_type>);
+ static_assert(!is_array_v<value_type>);
+ static_assert(!is_same_v<value_type, in_place_t>);
+ static_assert(!__is_inplace_type<value_type>::value);
+ static_assert(std::is_same_v<value_type, remove_cv_t<value_type>>,
+ "value_type must not be const or volatile qualified");
+
+ // [indirect.ctor], constructors
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect()
+ requires is_default_constructible_v<_Allocator>
+ : __p_(__allocate_owned_object(__alloc_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a)
+ : __alloc_(__a), __p_(__allocate_owned_object(__alloc_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect(const indirect& __other)
+ : __alloc_(allocator_traits<_Allocator>::select_on_container_copy_construction(__other.__alloc_)),
+ __p_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, const indirect& __other)
+ : __alloc_(__a), __p_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect(indirect&& __other) noexcept
+ : __alloc_(std::move(__other.__alloc_)), __p_(std::exchange(__other.__p_, nullptr)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, indirect&& __other) noexcept
+ requires allocator_traits<_Allocator>::is_always_equal::value
+ : __alloc_(__a), __p_(std::exchange(__other.__p_, nullptr)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, indirect&& __other) : __alloc_(__a) {
+ if (__other.valueless_after_move()) {
+ __p_ = nullptr;
+ } else if (__alloc_ == __other.__alloc_) {
+ __p_ = std::exchange(__other.__p_, nullptr);
+ } else {
+ __p_ = __allocate_owned_object(__alloc_, *std::move(__other));
+ __other.__destroy_owned_object();
+ __other.__p_ = nullptr;
+ }
+ }
+
+ template <class _U = _Tp>
+ requires(!is_same_v<remove_cvref_t<_U>, indirect> && !is_same_v<remove_cvref_t<_U>, in_place_t> &&
+ is_constructible_v<_Tp, _U> && is_default_constructible_v<_Allocator>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(_U&& __u)
+ : __p_(__allocate_owned_object(__alloc_, std::forward<_U>(__u))) {}
+
+ template <class _U = _Tp>
+ requires(!is_same_v<remove_cvref_t<_U>, indirect> && !is_same_v<remove_cvref_t<_U>, in_place_t> &&
+ is_constructible_v<_Tp, _U>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, _U&& __u)
+ : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_U>(__u))) {}
+
+ template <class... _Us>
+ requires(is_constructible_v<_Tp, _Us...> && is_default_constructible_v<_Allocator>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, _Us&&... __us)
+ : __p_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {}
+
+ template <class... _Us>
+ requires is_constructible_v<_Tp, _Us...>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, in_place_t, _Us&&... __us)
+ : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {}
+
+ template <class _I, class... _Us>
+ requires(is_constructible_v<_Tp, initializer_list<_I>&, _Us...> && is_default_constructible_v<_Allocator>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, initializer_list<_I> __ilist, _Us&&... __us)
+ : __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
+
+ template <class _I, class... _Us>
+ requires is_constructible_v<_Tp, initializer_list<_I>&, _Us...>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(
+ allocator_arg_t, const _Allocator& __a, in_place_t, initializer_list<_I> __ilist, _Us&&... __us)
+ : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
+
+ // [indirect.dtor], destructor
+ _LIBCPP_HIDE_FROM_ABI constexpr ~indirect() { __destroy_owned_object(); }
+
+ // [indirect.assign], assignment
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(const indirect& __other) {
+ if (std::addressof(__other) == this)
+ return *this;
+
+ static constexpr bool __propagate_allocator =
+ allocator_traits<_Allocator>::propagate_on_container_copy_assignment::value;
+ if (__other.valueless_after_move()) {
+ __destroy_owned_object();
+ __p_ = nullptr;
+ } else if (!valueless_after_move() && __alloc_ == __other.__alloc_) {
+ *__p_ = *__other;
+ } else {
+ pointer __new_p;
+ if constexpr (__propagate_allocator) {
+ // We need a mutable instance of the allocator, so make a copy.
+ _Allocator __alloc_copy = __other.__alloc_;
+ __new_p = __allocate_owned_object(__alloc_copy, *__other);
+ } else {
+ __new_p = __allocate_owned_object(__alloc_, *__other);
+ }
+ __destroy_owned_object();
+ __p_ = __new_p;
+ }
+
+ if constexpr (__propagate_allocator)
+ __alloc_ = __other.__alloc_;
+
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(indirect&& __other) noexcept(
+ allocator_traits<_Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<_Allocator>::is_always_equal::value) {
+ if (std::addressof(__other) == this)
+ return *this;
+
+ static constexpr bool __propagate_allocator =
+ allocator_traits<_Allocator>::propagate_on_container_move_assignment::value;
+
+ pointer __new_p;
+ if constexpr (__propagate_allocator || allocator_traits<_Allocator>::is_always_equal::value) {
+ __new_p = __other.__p_;
+ } else if (__other.valueless_after_move()) {
+ __new_p = nullptr;
+ } else if (__alloc_ == __other.__alloc_) {
+ __new_p = __other.__p_;
+ } else {
+ __new_p = __allocate_owned_object(__alloc_, *std::move(__other));
+ __other.__destroy_owned_object();
+ }
+ __other.__p_ = nullptr;
+ __destroy_owned_object();
+ __p_ = __new_p;
+
+ if constexpr (__propagate_allocator)
+ __alloc_ = __other.__alloc_;
+
+ return *this;
+ }
+
+ template <class _U = _Tp>
+ requires(!is_same_v<remove_cvref_t<_U>, indirect> && is_constructible_v<_Tp, _U> && is_assignable_v<_Tp&, _U>)
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(_U&& __u) {
+ if (valueless_after_move())
+ __p_ = __allocate_owned_object(__alloc_, std::forward<_U>(__u));
+ else
+ *__p_ = std::forward<_U>(__u);
+ return *this;
+ }
+
+ // [indirect.obs], observers
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ !valueless_after_move(), "operator* called on a valueless std::indirect object");
+ return *__p_;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ !valueless_after_move(), "operator* called on a valueless std::indirect object");
+ return *__p_;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ !valueless_after_move(), "operator* called on a valueless std::indirect object");
+ return std::move(*__p_);
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ !valueless_after_move(), "operator* called on a valueless std::indirect object");
+ return std::move(*__p_);
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_pointer operator->() const noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ !valueless_after_move(), "operator-> called on a valueless std::indirect object");
+ return __p_;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr pointer operator->() noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ !valueless_after_move(), "operator-> called on a valueless std::indirect object");
+ return __p_;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool valueless_after_move() const noexcept { return !__p_; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr allocator_type get_allocator() const noexcept { return __alloc_; }
+
+ // [indirect.swap], swap
+ _LIBCPP_HIDE_FROM_ABI constexpr void
+ swap(indirect& __other) noexcept(allocator_traits<_Allocator>::propagate_on_container_swap::value ||
+ allocator_traits<_Allocator>::is_always_equal::value) {
+ _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
+ allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(),
+ "swapping std::indirect objects with different allocators");
+ std::swap(__p_, __other.__p_);
+ std::__swap_allocator(__alloc_, __other.__alloc_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr void
+ swap(indirect& __lhs, indirect& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) {
+ __lhs.swap(__rhs);
+ }
+
+ // [indirect.relops], relational operators
+ template <class _U, class _AA>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const indirect& __lhs, const indirect<_U, _AA>& __rhs) noexcept(noexcept(*__lhs == *__rhs)) {
+ return (__lhs.valueless_after_move() == __rhs.valueless_after_move()) &&
+ (__lhs.valueless_after_move() || *__lhs == *__rhs);
+ }
+
+ template <class _U, class _AA>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _U>
+ operator<=>(const indirect& __lhs, const indirect<_U, _AA>& __rhs) {
+ if (__lhs.valueless_after_move() || __rhs.valueless_after_move())
+ return !__lhs.valueless_after_move() <=> !__rhs.valueless_after_move();
+ return std::__synth_three_way(*__lhs, *__rhs);
+ }
+
+ // [indirect.comp.with.t], comparison with T
+ template <class _U>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const indirect& __lhs, const _U& __rhs) noexcept(noexcept(*__lhs == __rhs)) {
+ return !__lhs.valueless_after_move() && *__lhs == __rhs;
+ }
+
+ template <class _U>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _U>
+ operator<=>(const indirect& __lhs, const _U& __rhs) {
+ return __lhs.valueless_after_move() ? strong_ordering::less : std::__synth_three_way(*__lhs, __rhs);
+ }
+
+private:
+ template <class... _Us>
+ _LIBCPP_HIDE_FROM_ABI static constexpr pointer __allocate_owned_object(_Allocator& __a, _Us&&... __us) {
+ __allocation_guard<_Allocator> __guard(__a, 1);
+ allocator_traits<_Allocator>::construct(__a, __guard.__get(), std::forward<_Us>(__us)...);
+ return __guard.__release_ptr();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_owned_object() noexcept {
+ if (!valueless_after_move()) {
+ allocator_traits<_Allocator>::destroy(__alloc_, __p_);
+ allocator_traits<_Allocator>::deallocate(__alloc_, __p_, 1);
+ }
+ }
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _Allocator __alloc_ = _Allocator();
+ pointer __p_;
+};
+
+template <class _Value>
+indirect(_Value) -> indirect<_Value>;
+
+template <class _Allocator, class _Value>
+indirect(allocator_arg_t, _Allocator, _Value) -> indirect<_Value, __rebind_alloc<allocator_traits<_Allocator>, _Value>>;
+
+template <class _T, class _Allocator>
+ requires is_default_constructible_v<hash<_T>>
+struct hash<indirect<_T, _Allocator>> {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const indirect<_T, _Allocator>& __i) const {
+ return __i.valueless_after_move() ? 0 : hash<_T>()(*__i);
+ }
+};
+
+namespace pmr {
+
+template <class _Tp>
+using indirect _LIBCPP_AVAILABILITY_PMR = indirect<_Tp, polymorphic_allocator<_Tp>>;
+
+} // namespace pmr
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___MEMORY_INDIRECT_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index ca880c83a544d..a13b3071163c9 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -931,6 +931,92 @@ template<class Smart, class Pointer, class... Args>
template<class Pointer = void, class Smart, class... Args>
auto inout_ptr(Smart& s, Args&&... args); // since c++23
+// [indirect], class template indirect
+template<class T, class Allocator = allocator<T>>
+class indirect // since C++26
+{
+public:
+ using value_type = T;
+ using allocator_type = Allocator;
+ using pointer = allocator_traits<Allocator>::pointer;
+ using const_pointer = allocator_traits<Allocator>::const_pointer;
+
+ // [indirect.ctor], constructors
+ constexpr explicit indirect();
+ constexpr explicit indirect(allocator_arg_t, const Allocator& a);
+ constexpr indirect(const indirect& other);
+ constexpr indirect(allocator_arg_t, const Allocator& a, const indirect& other);
+ constexpr indirect(indirect&& other) noexcept;
+ constexpr indirect(allocator_arg_t, const Allocator& a, indirect&& other)
+ noexcept(see below);
+ template<class U = T>
+ constexpr explicit indirect(U&& u);
+ template<class U = T>
+ constexpr explicit indirect(allocator_arg_t, const Allocator& a, U&& u);
+ template<class... Us>
+ constexpr explicit indirect(in_place_t, Us&&... us);
+ template<class... Us>
+ constexpr explicit indirect(allocator_arg_t, const Allocator& a,
+ in_place_t, Us&&... us);
+ template<class I, class... Us>
+ constexpr explicit indirect(in_place_t, initializer_list<I> ilist, Us&&... us);
+ template<class I, class... Us>
+ constexpr explicit indirect(allocator_arg_t, const Allocator& a,
+ in_place_t, initializer_list<I> ilist, Us&&... us);
+
+ // [indirect.dtor], destructor
+ constexpr ~indirect();
+
+ // [indirect.assign], assignment
+ constexpr indirect& operator=(const indirect& other);
+ constexpr indirect& operator=(indirect&& other) noexcept(see below);
+ template<class U = T>
+ constexpr indirect& operator=(U&& u);
+
+ // [indirect.obs], observers
+ constexpr const T& operator*() const & noexcept;
+ constexpr T& operator*() & noexcept;
+ constexpr const T&& operator*() const && noexcept;
+ constexpr T&& operator*() && noexcept;
+ constexpr const_pointer operator->() const noexcept;
+ constexpr pointer operator->() noexcept;
+ constexpr bool valueless_after_move() const noexcept;
+ constexpr allocator_type get_allocator() const noexcept;
+
+ // [indirect.swap], swap
+ constexpr void swap(indirect& other) noexcept(see below);
+ friend constexpr void swap(indirect& lhs, indirect& rhs) noexcept(see below);
+
+ // [indirect.relops], relational operators
+ template<class U, class AA>
+ friend constexpr bool operator==(const indirect& lhs, const indirect<U, AA>& rhs)
+ noexcept(see below);
+ template<class U, class AA>
+ friend constexpr auto operator<=>(const indirect& lhs, const indirect<U, AA>& rhs)
+ -> synth-three-way-result<T, U>;
+
+ // [indirect.comp.with.t], comparison with T
+ template<class U>
+ friend constexpr bool operator==(const indirect& lhs, const U& rhs) noexcept(see below);
+ template<class U>
+ friend constexpr auto operator<=>(const indirect& lhs, const U& rhs)
+ -> synth-three-way-result<T, U>;
+};
+
+template<class Value>
+ indirect(Value) -> indirect<Value>;
+
+template<class Allocator, class Value>
+ indirect(allocator_arg_t, Allocator, Value)
+ -> indirect<Value, typename allocator_traits<Allocator>::template rebind_alloc<Value>>;
+
+// [indirect.hash], hash support
+template<class T, class Alloc> struct hash<indirect<T, Alloc>>; // since C++26
+
+namespace pmr {
+ template<class T> using indirect = indirect<T, polymorphic_allocator<T>>; // since C++26
+}
+
} // std
*/
@@ -978,6 +1064,10 @@ template<class Pointer = void, class Smart, class... Args>
# include <__memory/allocate_at_least.h>
# endif
+# if _LIBCPP_STD_VER >= 26
+# include <__memory/indirect.h>
+# endif
+
# include <version>
// [memory.syn]
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 24a2fe761943a..50a0cbde37a1e 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1654,6 +1654,7 @@ module std [system] {
module destroy { header "__memory/destroy.h" }
module destruct_n { header "__memory/destruct_n.h" }
module fwd { header "__fwd/memory.h" }
+ module indirect { header "__memory/indirect.h" }
module inout_ptr { header "__memory/inout_ptr.h" }
module is_sufficiently_aligned { header "__memory/is_sufficiently_aligned.h" }
module noexcept_move_assign_container { header "__memory/noexcept_move_assign_container.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index b0030602f854a..c7f34d3be3dd0 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -138,6 +138,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_indirect 202502L <memory>
__cpp_lib_inplace_vector 202406L <inplace_vector>
__cpp_lib_int_pow2 202002L <bit>
__cpp_lib_integer_comparison_functions 202002L <utility>
@@ -581,6 +582,7 @@ __cpp_lib_void_t 201411L <type_traits>
// # define __cpp_lib_function_ref 202306L
// # define __cpp_lib_generate_random 202403L
// # define __cpp_lib_hazard_pointer 202306L
+# define __cpp_lib_indirect 202502L
// # define __cpp_lib_inplace_vector 202406L
# define __cpp_lib_is_sufficiently_aligned 202411L
# if __has_builtin(__builtin_is_virtual_base_of)
diff --git a/libcxx/modules/std/memory.inc b/libcxx/modules/std/memory.inc
index c25e9e3443e9c..41829b2c2c29d 100644
--- a/libcxx/modules/std/memory.inc
+++ b/libcxx/modules/std/memory.inc
@@ -194,6 +194,15 @@ export namespace std {
using std::inout_ptr;
#endif // _LIBCPP_STD_VER >= 23
+#if _LIBCPP_STD_VER >= 26
+ // [indirect], class template indirect
+ using std::indirect;
+
+ namespace pmr {
+ using std::pmr::indirect;
+ }
+#endif // _LIBCPP_STD_VER >= 26
+
#if _LIBCPP_HAS_THREADS
// [depr.util.smartptr.shared.atomic]
using std::atomic_is_lock_free;
diff --git a/libcxx/test/libcxx/memory/indirect/indirect.obs/assert.arrow.pass.cpp b/libcxx/test/libcxx/memory/indirect/indirect.obs/assert.arrow.pass.cpp
new file mode 100644
index 0000000000000..bf81ee6cc829e
--- /dev/null
+++ b/libcxx/test/libcxx/memory/indirect/indirect.obs/assert.arrow.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
+
+// <memory>
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <memory>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ struct S {
+ int n = 0;
+ };
+ std::indirect<S> i;
+ auto(std::move(i));
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(i->n, "operator-> called on a valueless std::indirect object");
+ }
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(std::as_const(i)->n, "operator-> called on a valueless std::indirect object");
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/memory/indirect/indirect.obs/assert.deref.pass.cpp b/libcxx/test/libcxx/memory/indirect/indirect.obs/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..061ea62d8ad4a
--- /dev/null
+++ b/libcxx/test/libcxx/memory/indirect/indirect.obs/assert.deref.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <memory>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ std::indirect<int> i;
+ auto(std::move(i));
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(*i, "operator* called on a valueless std::indirect object");
+ }
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(*std::move(i), "operator* called on a valueless std::indirect object");
+ }
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(i), "operator* called on a valueless std::indirect object");
+ }
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(*std::move(std::as_const(i)), "operator* called on a valueless std::indirect object");
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/memory/indirect/indirect.obs/deref.nodiscard.verify.cpp b/libcxx/test/libcxx/memory/indirect/indirect.obs/deref.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..4413237b7918e
--- /dev/null
+++ b/libcxx/test/libcxx/memory/indirect/indirect.obs/deref.nodiscard.verify.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++26
+
+// <memory>
+
+// Test the libc++ extension that std::indirect<T>::operator* is marked as [[nodiscard]].
+
+#include <memory>
+#include <utility>
+
+void test(std::indirect<int>& i) {
+ // clang-format off
+ *i; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ *std::move(i); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ *std::as_const(i); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ *std::move(std::as_const(i)); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ // clang-format on
+}
diff --git a/libcxx/test/libcxx/memory/indirect/indirect.obs/get_allocator.nodiscard.verify.cpp b/libcxx/test/libcxx/memory/indirect/indirect.obs/get_allocator.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..dbe0f30778593
--- /dev/null
+++ b/libcxx/test/libcxx/memory/indirect/indirect.obs/get_allocator.nodiscard.verify.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// Test the libc++ extension that std::indirect<T>::get_allocator is marked as [[nodiscard]].
+
+#include <memory>
+
+void test(std::indirect<int>& i) {
+ i.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/libcxx/memory/indirect/indirect.obs/valueless_after_move.nodiscard.verify.cpp b/libcxx/test/libcxx/memory/indirect/indirect.obs/valueless_after_move.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6dded10bcde65
--- /dev/null
+++ b/libcxx/test/libcxx/memory/indirect/indirect.obs/valueless_after_move.nodiscard.verify.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// Test the libc++ extension that std::indirect<T>::valueless_after_move is marked as [[nodiscard]].
+
+#include <memory>
+
+void test(std::indirect<int>& i) {
+ i.valueless_after_move(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/libcxx/memory/indirect/indirect.swap/assert.swap.pass.cpp b/libcxx/test/libcxx/memory/indirect/indirect.swap/assert.swap.pass.cpp
new file mode 100644
index 0000000000000..d0c5a2fb6db62
--- /dev/null
+++ b/libcxx/test/libcxx/memory/indirect/indirect.swap/assert.swap.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
+
+// <memory>
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <memory>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ std::indirect<int, test_allocator<int>> i1(std::allocator_arg, test_allocator<int>(1));
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(2));
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(swap(i1, i2), "swapping std::indirect objects with different allocators");
+ }
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(i1.swap(i2), "swapping std::indirect objects with different allocators");
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/memory/indirect/no_specializations.verify.cpp b/libcxx/test/libcxx/memory/indirect/no_specializations.verify.cpp
new file mode 100644
index 0000000000000..cb6e3dfdf2f53
--- /dev/null
+++ b/libcxx/test/libcxx/memory/indirect/no_specializations.verify.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++26
+
+// Check that user-specializations are diagnosed
+// See [indirect.general]/8
+
+#include <memory>
+
+#if !__has_warning("-Winvalid-specialization")
+// expected-no-diagnostics
+#else
+struct S {};
+
+template <>
+class std::indirect<S>; // expected-error {{cannot be specialized}}
+#endif
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 f287e1ad9b3ad..518a1f37e1f51 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
@@ -52,6 +52,10 @@
# error "__cpp_lib_enable_shared_from_this should not be defined before c++17"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_is_sufficiently_aligned
# error "__cpp_lib_is_sufficiently_aligned should not be defined before c++26"
# endif
@@ -130,6 +134,10 @@
# error "__cpp_lib_enable_shared_from_this should not be defined before c++17"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_is_sufficiently_aligned
# error "__cpp_lib_is_sufficiently_aligned should not be defined before c++26"
# endif
@@ -223,6 +231,10 @@
# error "__cpp_lib_enable_shared_from_this should have the value 201603L in c++17"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_is_sufficiently_aligned
# error "__cpp_lib_is_sufficiently_aligned should not be defined before c++26"
# endif
@@ -337,6 +349,10 @@
# error "__cpp_lib_enable_shared_from_this should have the value 201603L in c++20"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_is_sufficiently_aligned
# error "__cpp_lib_is_sufficiently_aligned should not be defined before c++26"
# endif
@@ -463,6 +479,10 @@
# error "__cpp_lib_enable_shared_from_this should have the value 201603L in c++23"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_is_sufficiently_aligned
# error "__cpp_lib_is_sufficiently_aligned should not be defined before c++26"
# endif
@@ -592,6 +612,13 @@
# error "__cpp_lib_enable_shared_from_this should have the value 201603L in c++26"
# endif
+# ifndef __cpp_lib_indirect
+# error "__cpp_lib_indirect should be defined in c++26"
+# endif
+# if __cpp_lib_indirect != 202502L
+# error "__cpp_lib_indirect should have the value 202502L in c++26"
+# endif
+
# ifndef __cpp_lib_is_sufficiently_aligned
# error "__cpp_lib_is_sufficiently_aligned 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 8189c5c4e5985..4bb383a2df16b 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
@@ -432,6 +432,10 @@
# error "__cpp_lib_incomplete_container_elements should not be defined before c++17"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_inplace_vector
# error "__cpp_lib_inplace_vector should not be defined before c++26"
# endif
@@ -1358,6 +1362,10 @@
# error "__cpp_lib_incomplete_container_elements should not be defined before c++17"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_inplace_vector
# error "__cpp_lib_inplace_vector should not be defined before c++26"
# endif
@@ -2422,6 +2430,10 @@
# error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++17"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_inplace_vector
# error "__cpp_lib_inplace_vector should not be defined before c++26"
# endif
@@ -3744,6 +3756,10 @@
# error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++20"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_inplace_vector
# error "__cpp_lib_inplace_vector should not be defined before c++26"
# endif
@@ -5258,6 +5274,10 @@
# error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++23"
# endif
+# ifdef __cpp_lib_indirect
+# error "__cpp_lib_indirect should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_inplace_vector
# error "__cpp_lib_inplace_vector should not be defined before c++26"
# endif
@@ -7126,6 +7146,13 @@
# error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++26"
# endif
+# ifndef __cpp_lib_indirect
+# error "__cpp_lib_indirect should be defined in c++26"
+# endif
+# if __cpp_lib_indirect != 202502L
+# error "__cpp_lib_indirect should have the value 202502L in c++26"
+# endif
+
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_inplace_vector
# error "__cpp_lib_inplace_vector should be defined in c++26"
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.assign/copy.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.assign/copy.pass.cpp
new file mode 100644
index 0000000000000..2ac554fefbe42
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.assign/copy.pass.cpp
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr indirect& operator=(const indirect& other);
+
+#include <cassert>
+#include <type_traits>
+#include <memory>
+
+#include "test_convertible.h"
+#include "test_allocator.h"
+#include "min_allocator.h"
+#include "archetypes.h"
+
+constexpr void test_assignment() {
+ { // Assigning from a valueless indirect destroys the owned object, if any.
+ std::indirect<int> i1;
+ std::indirect<int> i2;
+
+ auto(std::move(i2));
+ i1 = i2;
+ assert(i1.valueless_after_move());
+ assert(i2.valueless_after_move());
+ i1 = i2;
+ assert(i1.valueless_after_move());
+ assert(i2.valueless_after_move());
+ }
+ { // Assigning to an indirect that already owns an object doesn't allocate a new object.
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i1(std::allocator_arg, test_allocator<int>(&stats), 1);
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(&stats), 2);
+ assert(stats.construct_count == 2);
+ auto* addr_before = &*i1;
+ i1 = i2;
+ assert(addr_before == &*i1);
+ assert(stats.construct_count == 2);
+ assert(*i1 == 2);
+ assert(*i2 == 2);
+ }
+ { // Assigning to an indirect with a different allocator allocates a new owned object.
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i1(std::allocator_arg, test_allocator<int>(1, &stats), 1);
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(2, &stats), 2);
+ assert(stats.construct_count == 2);
+ auto* addr_before = &*i1;
+ i1 = i2;
+ assert(addr_before != &*i1);
+ assert(stats.construct_count == 3);
+ assert(*i1 == 2);
+ assert(*i2 == 2);
+ }
+ { // Assigning to a valueless indirect allocates a new owned object.
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i1(std::allocator_arg, test_allocator<int>(&stats), 1);
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(&stats), 2);
+ assert(stats.construct_count == 2);
+ auto(std::move(i1));
+ i1 = i2;
+ assert(*i1 == 2);
+ assert(*i2 == 2);
+ assert(stats.construct_count == 3);
+ }
+ { // Assignment returns *this.
+ std::indirect<int> i1;
+ const std::indirect<int> i2;
+ std::same_as<std::indirect<int>&> decltype(auto) addr = (i1 = i2);
+ assert(&addr == &i1);
+ }
+}
+
+void test_assignment_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct CopyingThrows {
+ int i = 0;
+ CopyingThrows(int n) : i(n) {}
+ CopyingThrows(const CopyingThrows&) { throw 42; }
+ CopyingThrows& operator=(const CopyingThrows&) { throw 42; }
+ };
+
+ std::indirect<CopyingThrows, test_allocator<CopyingThrows>> i1(
+ std::allocator_arg, test_allocator<CopyingThrows>(1), 1);
+ std::indirect<CopyingThrows, test_allocator<CopyingThrows>> i2(
+ std::allocator_arg, test_allocator<CopyingThrows>(2), 2);
+ auto* addr1 = &*i1;
+ auto* addr2 = &*i2;
+ try {
+ i1 = i2;
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+ assert(addr1 == &*i1);
+ assert(addr2 == &*i2);
+ assert(i1->i == 1);
+ assert(i2->i == 2);
+ assert(i1.get_allocator().get_data() == 1);
+ assert(i2.get_allocator().get_data() == 2);
+#endif
+}
+
+constexpr bool test() {
+ test_assignment();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_assignment_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.assign/move.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.assign/move.pass.cpp
new file mode 100644
index 0000000000000..5a9c4b2823431
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.assign/move.pass.cpp
@@ -0,0 +1,167 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr indirect& operator=(indirect&& other)
+// noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+// allocator_traits<Allocator>::is_always_equal::value);
+
+#include <cassert>
+#include <type_traits>
+#include <memory>
+
+#include "test_convertible.h"
+#include "test_allocator.h"
+#include "min_allocator.h"
+#include "archetypes.h"
+
+constexpr void test_assignment() {
+ { // Move-assigning from an indirect leaves it valueless.
+ std::indirect<int> i1(1);
+ std::indirect<int> i2(2);
+
+ i1 = std::move(i2); // Both RHS and LHS hold values.
+ assert(*i1 == 2);
+ assert(i2.valueless_after_move());
+ i1 = std::move(i2); // LHS holds value, RHS is valueless.
+ assert(i1.valueless_after_move());
+ assert(i2.valueless_after_move());
+ i1 = std::move(i2); // Both RHS and LHS are valueless.
+ assert(i1.valueless_after_move());
+ assert(i2.valueless_after_move());
+ }
+ { // Move assigning to an indirect simply transfers ownership of the held object.
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i1(std::allocator_arg, test_allocator<int>(&stats), 1);
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(&stats), 2);
+ assert(stats.construct_count == 2);
+ auto* addr2 = &*i2;
+ i1 = std::move(i2);
+ assert(i2.valueless_after_move());
+ assert(&*i1 == addr2);
+ assert(stats.construct_count == 2);
+ assert(*i1 == 2);
+ }
+ { // Assigning to an indirect with a different, non-POCMA allocator allocates a new owned object.
+ std::indirect<int, test_allocator<int>> i1(std::allocator_arg, test_allocator<int>(1), 1);
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(2), 2);
+ auto* addr1 = &*i1;
+ auto* addr2 = &*i2;
+ i1 = std::move(i2);
+ static_assert(!noexcept(i1 = std::move(i2)));
+ assert(i2.valueless_after_move());
+ assert(&*i1 != addr1);
+ assert(&*i1 != addr2);
+ }
+ { // Assignment returns *this.
+ std::indirect<int> i1;
+ std::indirect<int> i2;
+ std::same_as<std::indirect<int>&> decltype(auto) addr = (i1 = std::move(i2));
+ assert(&addr == &i1);
+ }
+}
+
+void test_assignment_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct MoveThrows {
+ int i = 0;
+ MoveThrows(int n) : i(n) {}
+ MoveThrows(MoveThrows&&) { throw 42; }
+ MoveThrows& operator=(MoveThrows&&) { throw 42; }
+ };
+
+ std::indirect<MoveThrows, test_allocator<MoveThrows>> i1(std::allocator_arg, test_allocator<MoveThrows>(1), 1);
+ std::indirect<MoveThrows, test_allocator<MoveThrows>> i2(std::allocator_arg, test_allocator<MoveThrows>(2), 2);
+ auto* addr1 = &*i1;
+ auto* addr2 = &*i2;
+ try {
+ i1 = std::move(i2);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+ assert(addr1 == &*i1);
+ assert(addr2 == &*i2);
+ assert(i1->i == 1);
+ assert(i2->i == 2);
+ assert(i1.get_allocator().get_data() == 1);
+ assert(i2.get_allocator().get_data() == 2);
+#endif
+}
+
+template <class T>
+struct pocma_test_allocator : test_allocator<T> {
+ using test_allocator<T>::test_allocator;
+ using propagate_on_container_move_assignment = std::true_type;
+};
+
+struct Immovable {
+ Immovable() = default;
+ Immovable(const Immovable&) = delete;
+ Immovable(Immovable&&) = delete;
+ Immovable& operator=(const Immovable&) = delete;
+ Immovable& operator=(Immovable&&) = delete;
+};
+
+// https://cplusplus.github.io/LWG/issue4251
+constexpr void test_lwg4251() {
+ { // Move assigning indirect<T> doesn't require T to be copy constructible.
+ struct NotCopyConstructible {
+ constexpr NotCopyConstructible() = default;
+ constexpr NotCopyConstructible(NotCopyConstructible&&) {}
+ };
+ static_assert(!std::is_copy_constructible_v<NotCopyConstructible>);
+ std::indirect<NotCopyConstructible, test_allocator<NotCopyConstructible>> i1;
+ std::indirect<NotCopyConstructible, test_allocator<NotCopyConstructible>> i2;
+ i1 = std::move(i2);
+ }
+ { // T doesn't have to be move constructible, as long as the allocator propagates on move.
+ using A = other_allocator<Immovable>;
+ static_assert(std::allocator_traits<A>::propagate_on_container_move_assignment::value);
+ static_assert(!std::allocator_traits<A>::is_always_equal::value);
+ std::indirect<Immovable, A> i;
+ i = std::move(i);
+ }
+ { // T doesn't have to be move constructible, as long as the allocator is always equal.
+ using A = explicit_allocator<Immovable>;
+ static_assert(!std::allocator_traits<A>::propagate_on_container_move_assignment::value);
+ static_assert(std::allocator_traits<A>::is_always_equal::value);
+ std::indirect<Immovable, A> i;
+ i = std::move(i);
+ }
+ { // Move assignment with a POCMA allocator simply transfers ownership instead of allocating a new object.
+ std::indirect<int, other_allocator<int>> i1(1);
+ std::indirect<int, other_allocator<int>> i2(2);
+ auto* addr2 = &*i2;
+ i1 = std::move(i2);
+ assert(i2.valueless_after_move());
+ assert(*i1 == 2);
+ assert(&*i1 == addr2);
+ }
+}
+
+constexpr bool test() {
+ test_assignment();
+ test_lwg4251();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_assignment_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.assign/perfect_forwarding.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.assign/perfect_forwarding.pass.cpp
new file mode 100644
index 0000000000000..b5697952c2c4a
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.assign/perfect_forwarding.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class U = T>
+// constexpr indirect& operator=(U&& u);
+
+#include <cassert>
+#include <type_traits>
+#include <memory>
+
+#include "test_convertible.h"
+#include "test_allocator.h"
+#include "min_allocator.h"
+#include "archetypes.h"
+
+struct MoveConstructibleOnly {
+ MoveConstructibleOnly(MoveConstructibleOnly&&) {}
+};
+
+constexpr void test_assignment_sfinae() {
+ { // Assignment isn't enabled if T is constructible but not assignable from the RHS type.
+ using I = std::indirect<MoveConstructibleOnly>;
+ static_assert(!std::is_assignable_v<I&, MoveConstructibleOnly&>);
+ static_assert(!std::is_assignable_v<I&, MoveConstructibleOnly&&>);
+ }
+ { // Assignment isn't enabled if T is assignable but not constructible from the RHS type.
+ using I = std::indirect<TestTypes::MoveAssignOnly>;
+ static_assert(!std::is_assignable_v<I&, TestTypes::MoveAssignOnly&>);
+ static_assert(!std::is_assignable_v<I&, TestTypes::MoveAssignOnly&&>);
+ }
+ {
+ using I = std::indirect<TestTypes::MoveOnly>;
+ static_assert(!std::is_assignable_v<I&, TestTypes::MoveOnly&>);
+ static_assert(std::is_assignable_v<I&, TestTypes::MoveOnly&&>);
+ }
+}
+
+constexpr void test_assignment() {
+ { // Assigning to an indirect that holds a value doesn't allocate a new object.
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i(std::allocator_arg, test_allocator<int>(&stats), 42);
+ assert(stats.construct_count == 1);
+ i = 10;
+ assert(stats.construct_count == 1);
+ assert(*i == 10);
+ }
+ { // Assigning to a valueless indirect allocates a new owned object.
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i(std::allocator_arg, test_allocator<int>(&stats), 42);
+ auto(std::move(i));
+ assert(stats.construct_count == 1);
+ i = 10;
+ assert(stats.construct_count == 2);
+ assert(*i == 10);
+ }
+ { // Assignment returns *this.
+ std::indirect<int> i;
+ std::same_as<std::indirect<int>&> decltype(auto) ret = (i = 10);
+ assert(&ret == &i);
+ }
+}
+
+void test_assignment_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct CopyingThrows {
+ CopyingThrows() = default;
+ CopyingThrows(const CopyingThrows&) { throw 42; }
+ CopyingThrows& operator=(const CopyingThrows&) { throw 42; }
+ };
+
+ std::indirect<CopyingThrows> i;
+ try {
+ i = CopyingThrows();
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+
+#endif
+}
+
+constexpr bool test() {
+ test_assignment_sfinae();
+ test_assignment();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_assignment_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/eq.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/eq.pass.cpp
new file mode 100644
index 0000000000000..31f1bfc4b1253
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/eq.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class U>
+// constexpr bool operator==(const indirect& lhs, const U& rhs) noexcept(noexcept(*lhs == rhs));
+
+#include <cassert>
+#include <memory>
+#include <utility>
+
+#include "test_comparisons.h"
+
+constexpr bool test() {
+ {
+ const std::indirect<int> i1(1);
+ const int i2 = 2;
+ assert(testEquality(i1, 2, false));
+ static_assert(noexcept(i1 == i2));
+ static_assert(noexcept(i2 == i1));
+ static_assert(noexcept(i1 != i2));
+ static_assert(noexcept(i2 != i1));
+ }
+ { // A valueless indirect always compares false.
+ std::indirect<int> i1;
+ const int i2 = 0;
+ assert(testEquality(i1, i2, true));
+ auto(std::move(i1));
+ assert(testEquality(i1, i2, false));
+ }
+
+ return true;
+}
+
+void test_comparison_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct ComparisonThrows {
+ int i = 0;
+ bool operator==(ComparisonThrows) const { throw 42; }
+ };
+
+ std::indirect<ComparisonThrows> i1(1);
+ ComparisonThrows i2(2);
+ static_assert(!noexcept(i1 == i2));
+ static_assert(!noexcept(i1 != i2));
+
+ try {
+ (void)(i1 == i2);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+
+ auto(std::move(i1));
+ assert(testEquality(i1, i2, false));
+#endif
+}
+
+int main(int, char**) {
+ test_comparison_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/three_way.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/three_way.pass.cpp
new file mode 100644
index 0000000000000..68588c5850bc8
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.comp.with.t/three_way.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++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class U>
+// constexpr synth-three-way-result<T, U>
+// operator<=>(const indirect& lhs, const U& rhs);
+
+#include <cassert>
+#include <compare>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "test_comparisons.h"
+
+constexpr bool test() {
+ {
+ const std::indirect<int> i1;
+ assert(testOrder(i1, -1, std::strong_ordering::greater));
+ assert(testOrder(i1, 0, std::strong_ordering::equal));
+ assert(testOrder(i1, 1, std::strong_ordering::less));
+ }
+ { // A valueless indirect always compares less than the object it is compared with.
+ std::indirect<int> i1;
+ auto(std::move(i1));
+ assert(testOrder(i1, std::numeric_limits<int>::min(), std::strong_ordering::less));
+ }
+
+ return true;
+}
+
+void test_comparison_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct ComparisonThrows {
+ int i = 0;
+ bool operator==(ComparisonThrows) const { throw 42; }
+ std::strong_ordering operator<=>(ComparisonThrows) const { throw 42; }
+ };
+
+ std::indirect<ComparisonThrows> i1(1);
+ ComparisonThrows i2(2);
+ try {
+ (void)(i1 <=> i2);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+
+ auto(std::move(i1));
+ assert(testOrder(i1, i2, std::strong_ordering::less));
+#endif
+}
+
+int main(int, char**) {
+ test_comparison_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/copy.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..0cbad0aca7b32
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/copy.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr indirect(const indirect& other)
+
+// constexpr indirect(allocator_arg_t, const Allocator& a, const indirect& other);
+
+#include <cassert>
+#include <memory>
+
+#include "test_allocator.h"
+#include "test_convertible.h"
+
+constexpr void test_copy_ctor_not_explicit() {
+ static_assert(test_convertible<std::indirect<int>, const std::indirect<int>&>());
+ static_assert(test_convertible<std::indirect<int>,
+ std::allocator_arg_t,
+ const std::allocator<int>&,
+ const std::indirect<int>&>());
+}
+
+constexpr void test_copy_ctor() {
+ {
+ const std::indirect<int> i1(42);
+ std::indirect<int> i2(i1);
+ assert(!i1.valueless_after_move());
+ assert(!i2.valueless_after_move());
+ assert(*i1 == 42);
+ assert(*i2 == 42);
+ assert(&*i1 != &*i2);
+ }
+ {
+ std::indirect<int> i1;
+ auto(std::move(i1));
+ assert(i1.valueless_after_move());
+ std::indirect<int> i2(i1);
+ assert(i2.valueless_after_move());
+ }
+ {
+ std::indirect<int, SocccAllocator<int>> i1;
+ assert(i1.get_allocator().count_ == 0);
+ std::indirect<int, SocccAllocator<int>> i2(i1);
+ assert(i2.get_allocator().count_ == 1);
+ }
+ {
+ const std::indirect<int> i1(42);
+ std::indirect<int> i2(std::allocator_arg, std::allocator<int>(), i1);
+ assert(!i1.valueless_after_move());
+ assert(!i2.valueless_after_move());
+ assert(*i1 == 42);
+ assert(*i2 == 42);
+ assert(&*i1 != &*i2);
+ }
+ {
+ const std::indirect<int, test_allocator<int>> i1;
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(42), i1);
+ assert(i1.get_allocator().get_data() == 0);
+ assert(i2.get_allocator().get_data() == 42);
+ }
+}
+
+void test_copy_ctor_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct CopyCtorThrows {
+ CopyCtorThrows() = default;
+ CopyCtorThrows(const CopyCtorThrows&) { throw 42; }
+ };
+
+ {
+ const std::indirect<CopyCtorThrows> i1;
+ try {
+ std::indirect<CopyCtorThrows> i2(i1);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+ }
+ {
+ const std::indirect<CopyCtorThrows> i1;
+ try {
+ std::indirect<CopyCtorThrows> i2(std::allocator_arg, std::allocator<int>(), i1);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+ }
+#endif
+}
+
+constexpr bool test() {
+ test_copy_ctor_not_explicit();
+ test_copy_ctor();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_copy_ctor_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/default.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/default.pass.cpp
new file mode 100644
index 0000000000000..02988da3ec369
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/default.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr explicit indirect();
+
+// constexpr explicit indirect(allocator_arg_t, const Allocator& a);
+
+#include <cassert>
+#include <type_traits>
+#include <memory>
+
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_convertible.h"
+
+constexpr void test_default_ctor_sfinae() {
+ static_assert(std::is_default_constructible_v<std::indirect<int>>);
+ static_assert(!std::is_default_constructible_v<std::indirect<int, no_default_allocator<int>>>);
+}
+
+constexpr void test_default_ctor_explicit() {
+ static_assert(only_explicitly_constructible_from<std::indirect<int>>);
+ static_assert(
+ only_explicitly_constructible_from<std::indirect<int>, std::allocator_arg_t, const std::allocator<int>&>);
+}
+
+constexpr void test_default_ctor() {
+ {
+ std::indirect<int> i;
+ assert(!i.valueless_after_move());
+ assert(*i == 0);
+ }
+ {
+ std::indirect<int, test_allocator<int>> i(std::allocator_arg, test_allocator<int>(42));
+ assert(!i.valueless_after_move());
+ assert(*i == 0);
+ assert(i.get_allocator().get_data() == 42);
+ }
+}
+
+void test_default_ctor_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct DefaultCtorThrows {
+ DefaultCtorThrows() { throw 42; }
+ };
+
+ try {
+ std::indirect<DefaultCtorThrows> i;
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+
+ try {
+ std::indirect<DefaultCtorThrows> i(std::allocator_arg, std::allocator<int>());
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+#endif
+}
+
+constexpr bool test() {
+ test_default_ctor_sfinae();
+ test_default_ctor_explicit();
+ test_default_ctor();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_default_ctor_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/in_place_t.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/in_place_t.pass.cpp
new file mode 100644
index 0000000000000..1ce18dd25097c
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/in_place_t.pass.cpp
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class... Us>
+// constexpr explicit indirect(in_place_t, Us&&... us);
+
+// template<class... Us>
+// constexpr explicit indirect(allocator_arg_t, const Allocator& a,
+// in_place_t, Us&& ...us);
+
+#include <cassert>
+#include <type_traits>
+#include <memory>
+
+#include "test_convertible.h"
+#include "test_allocator.h"
+#include "min_allocator.h"
+#include "archetypes.h"
+
+constexpr void test_in_place_t_ctor_sfinae() {
+ {
+ static_assert(
+ !std::is_constructible_v<std::indirect<TestTypes::MoveOnly>, std::in_place_t, const TestTypes::MoveOnly&>);
+ static_assert(std::is_constructible_v<std::indirect<TestTypes::MoveOnly>, std::in_place_t, TestTypes::MoveOnly&&>);
+ }
+ {
+ static_assert(std::is_constructible_v<std::indirect<int, no_default_allocator<int>>,
+ std::allocator_arg_t,
+ const no_default_allocator<int>&,
+ std::in_place_t,
+ int&>);
+ }
+ {
+ static_assert(!std::is_constructible_v<std::indirect<TestTypes::MoveOnly>,
+ std::allocator_arg_t,
+ const std::allocator<TestTypes::MoveOnly>&,
+ std::in_place_t,
+ const TestTypes::MoveOnly&>);
+ static_assert(std::is_constructible_v<std::indirect<TestTypes::MoveOnly>,
+ std::allocator_arg_t,
+ const std::allocator<TestTypes::MoveOnly>&,
+ std::in_place_t,
+ TestTypes::MoveOnly&&>);
+ }
+}
+
+constexpr void test_in_place_t_ctor_explicit() {
+ static_assert(only_explicitly_constructible_from<std::indirect<int>, std::in_place_t, int&>);
+ static_assert(only_explicitly_constructible_from<std::indirect<int>,
+ std::allocator_arg_t,
+ const std::allocator<TestTypes::MoveOnly>&,
+ std::in_place_t,
+ int&>);
+}
+
+constexpr void test_in_place_t_ctor() {
+ {
+ std::indirect<int> i(std::in_place, 42);
+ assert(!i.valueless_after_move());
+ assert(*i == 42);
+ }
+ {
+ std::indirect<std::pair<int, int>> i(std::in_place, 1, 2);
+ assert(!i.valueless_after_move());
+ assert((*i == std::pair{1, 2}));
+ }
+ {
+ std::indirect<int, test_allocator<int>> i(std::allocator_arg, test_allocator<int>(67), std::in_place, 42);
+ assert(!i.valueless_after_move());
+ assert(i.get_allocator().get_data() == 67);
+ assert(*i == 42);
+ }
+ {
+ std::indirect<std::pair<int, int>, test_allocator<std::pair<int, int>>> i(
+ std::allocator_arg, test_allocator<int>(67), std::in_place, 1, 2);
+ assert(!i.valueless_after_move());
+ assert(i.get_allocator().get_data() == 67);
+ assert((*i == std::pair{1, 2}));
+ }
+}
+
+void test_in_place_t_ctor_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct CopyCtorThrows {
+ CopyCtorThrows() = default;
+ CopyCtorThrows(const CopyCtorThrows&) { throw 42; }
+ };
+
+ CopyCtorThrows c;
+ try {
+ std::indirect<CopyCtorThrows> i(std::in_place, c);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+#endif
+}
+
+constexpr bool test() {
+ test_in_place_t_ctor_sfinae();
+ test_in_place_t_ctor_explicit();
+ test_in_place_t_ctor();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_in_place_t_ctor_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/init_list.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/init_list.pass.cpp
new file mode 100644
index 0000000000000..55026f8b4303d
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/init_list.pass.cpp
@@ -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++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class I, class... Us>
+// constexpr explicit indirect(in_place_t, initializer_list<I> ilist, Us&&... us);
+
+// template<class I, class... Us>
+// constexpr explicit indirect(allocator_arg_t, const Allocator& a,
+// in_place_t, initializer_list<I> ilist, Us&&... us);
+
+#include <cassert>
+#include <concepts>
+#include <initializer_list>
+#include <type_traits>
+#include <memory>
+
+#include "test_convertible.h"
+#include "test_allocator.h"
+#include "min_allocator.h"
+
+struct S {
+ constexpr S(std::same_as<std::initializer_list<int>&> auto&& ilist_arg, std::same_as<int> auto&& i)
+ : ilist(ilist_arg), int_addr(&i) {}
+
+ std::initializer_list<int> ilist;
+ int* int_addr;
+};
+
+constexpr void test_init_list_ctor_sfinae() {
+ {
+ using I = std::indirect<S>;
+ static_assert(std::is_constructible_v<I, std::in_place_t, std::initializer_list<int>, int&&>);
+ static_assert(std::is_constructible_v<I, std::in_place_t, const std::initializer_list<int>&, int&&>);
+ static_assert(!std::is_constructible_v<I, std::in_place_t, std::initializer_list<int>&, int&>);
+ }
+ {
+ using I = std::indirect<int, no_default_allocator<int>>;
+ static_assert(std::is_constructible_v<I, std::allocator_arg_t, const no_default_allocator<int>&, int&>);
+ }
+}
+
+constexpr void test_init_list_ctor_explicit() {
+ static_assert(
+ only_explicitly_constructible_from<std::indirect<int>, std::allocator_arg_t, const std::allocator<int>&, int&>);
+ static_assert(
+ only_explicitly_constructible_from<std::indirect<int>, std::allocator_arg_t, const std::allocator<int>&, int&&>);
+}
+
+constexpr void test_init_list_ctor() {
+ {
+ const std::initializer_list<int> ilist{1, 2};
+ int n = 0;
+ std::indirect<S> i(std::in_place, ilist, std::move(n));
+ assert(!i.valueless_after_move());
+ assert(i->ilist.begin() == ilist.begin());
+ assert(i->ilist.end() == ilist.end());
+ assert(i->int_addr == &n);
+ }
+ {
+ const std::initializer_list<int> ilist{1, 2};
+ int n = 0;
+ std::indirect<S, test_allocator<S>> i(
+ std::allocator_arg, test_allocator<S>(42), std::in_place, ilist, std::move(n));
+ assert(!i.valueless_after_move());
+ assert(i.get_allocator().get_data() == 42);
+ assert(i->ilist.begin() == ilist.begin());
+ assert(i->ilist.end() == ilist.end());
+ assert(i->int_addr == &n);
+ }
+}
+
+void test_init_list_ctor_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct CtorThrows {
+ CtorThrows(std::initializer_list<int>) { throw 42; }
+ };
+
+ try {
+ std::indirect<CtorThrows> i(std::in_place, std::initializer_list<int>{});
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+#endif
+}
+
+constexpr bool test() {
+ test_init_list_ctor_sfinae();
+ test_init_list_ctor_explicit();
+ test_init_list_ctor();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_init_list_ctor_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..a5d6059bf049c
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr indirect(indirect&& other) noexcept;
+
+// constexpr indirect(allocator_arg_t, const Allocator& a, indirect&& other)
+// noexcept(allocator_traits<Allocator>::is_always_equal::value);
+
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+#include "test_allocator.h"
+#include "test_convertible.h"
+
+constexpr void test_ctor_not_explicit() {
+ static_assert(test_convertible<std::indirect<int>, std::indirect<int>&&>());
+ static_assert(
+ test_convertible<std::indirect<int>, std::allocator_arg_t, const std::allocator<int>&, std::indirect<int>&&>());
+}
+
+constexpr void test_ctor_noexcept() {
+ static_assert(std::is_nothrow_constructible_v<std::indirect<int, test_allocator<int>>,
+ std::indirect<int, test_allocator<int>>&&>);
+ static_assert(std::is_nothrow_constructible_v<std::indirect<int>,
+ std::allocator_arg_t,
+ const std::allocator<int>&,
+ std::indirect<int>&&>);
+}
+
+constexpr void test_ctor() {
+ {
+ std::indirect<int> i1(42);
+
+ // Moving from an indirect leaves it valueless.
+ std::indirect<int> i2(std::move(i1));
+ assert(i1.valueless_after_move());
+ assert(!i2.valueless_after_move());
+ assert(*i2 == 42);
+
+ // Move constructing from a valueless indirect creates a new valueless indirect.
+ std::indirect<int> i3(std::move(i1));
+ assert(i1.valueless_after_move());
+ assert(i3.valueless_after_move());
+ }
+ {
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i1(10);
+
+ // If the allocators are equal, no memory is allocated.
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(&stats), std::move(i1));
+ assert(i1.valueless_after_move());
+ assert(!i2.valueless_after_move());
+ assert(*i2 == 10);
+ assert(stats.construct_count == 0);
+ }
+ {
+ test_allocator_statistics stats;
+ std::indirect<int, test_allocator<int>> i1(10);
+
+ // If the allocators aren't equal, a new owned object is constructed.
+ std::indirect<int, test_allocator<int>> i2(std::allocator_arg, test_allocator<int>(42, &stats), std::move(i1));
+ assert(i1.valueless_after_move());
+ assert(!i2.valueless_after_move());
+ assert(*i2 == 10);
+ assert(i1.get_allocator().get_data() == 0);
+ assert(i2.get_allocator().get_data() == 42);
+ assert(stats.construct_count == 1);
+
+ // If the source object is valueless, no memory is allocated, even if the allocators aren't equal.
+ std::indirect<int, test_allocator<int>> i3(std::allocator_arg, test_allocator<int>(67, &stats), std::move(i1));
+ assert(i1.valueless_after_move());
+ assert(i3.valueless_after_move());
+ assert(i1.get_allocator().get_data() == 0);
+ assert(i3.get_allocator().get_data() == 67);
+ assert(stats.construct_count == 1);
+ }
+ struct Incomplete;
+ { // Move construction doesn't require T to be complete.
+ (void)([](std::indirect<Incomplete>&& i) -> std::indirect<Incomplete> { return {std::move(i)}; });
+ }
+ { // Uses-allocator move construction doesn't require T to be complete as long as the allocator is always equal.
+ (void)([](std::indirect<Incomplete>&& i) -> std::indirect<Incomplete> {
+ return {std::allocator_arg, std::allocator<Incomplete>(), std::move(i)};
+ });
+ }
+}
+
+constexpr bool test() {
+ test_ctor_not_explicit();
+ test_ctor_noexcept();
+ test_ctor();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/perfect_forwarding.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/perfect_forwarding.pass.cpp
new file mode 100644
index 0000000000000..316180ae8a60b
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/perfect_forwarding.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class U = T>
+// constexpr explicit indirect(U&& u);
+
+// template<class U = T>
+// constexpr explicit indirect(allocator_arg_t, const Allocator& a, U&& u);
+
+#include <cassert>
+#include <type_traits>
+#include <memory>
+
+#include "test_convertible.h"
+#include "min_allocator.h"
+#include "archetypes.h"
+
+constexpr void test_perfect_forwarding_ctor_sfinae() {
+ {
+ using I = std::indirect<TestTypes::MoveOnly>;
+ static_assert(!std::is_constructible_v<I, TestTypes::MoveOnly&>);
+ static_assert(!std::is_constructible_v<I, const TestTypes::MoveOnly&>);
+ static_assert(std::is_constructible_v<I, TestTypes::MoveOnly&&>);
+ }
+ { // If the allocator isn't default-constructible, only the uses-allocator constructor is enabled.
+ using I = std::indirect<int, no_default_allocator<int>>;
+ static_assert(!std::is_constructible_v<I, int&>);
+ static_assert(std::is_constructible_v<I, std::allocator_arg_t, const no_default_allocator<int>&, int&>);
+ }
+}
+
+constexpr void test_perfect_forwarding_ctor_explicit() {
+ static_assert(only_explicitly_constructible_from<std::indirect<int>, int&>);
+ static_assert(only_explicitly_constructible_from<std::indirect<int, no_default_allocator<int>>,
+ std::allocator_arg_t,
+ const no_default_allocator<int>&,
+ int&>);
+}
+
+constexpr void test_perfect_forwarding_ctor() {
+ {
+ std::indirect<int> i(42);
+ assert(!i.valueless_after_move());
+ assert(*i == 42);
+ }
+ {
+ std::indirect<int> i(std::allocator_arg, std::allocator<int>(), 42);
+ assert(!i.valueless_after_move());
+ assert(*i == 42);
+ }
+}
+
+void test_perfect_forwarding_ctor_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct CopyCtorThrows {
+ CopyCtorThrows() = default;
+ CopyCtorThrows(const CopyCtorThrows&) { throw 42; }
+ };
+
+ try {
+ std::indirect<CopyCtorThrows> i(CopyCtorThrows{});
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+
+ try {
+ std::indirect<CopyCtorThrows> i(std::allocator_arg, std::allocator<CopyCtorThrows>(), CopyCtorThrows{});
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+#endif
+}
+
+constexpr bool test() {
+ test_perfect_forwarding_ctor_sfinae();
+ test_perfect_forwarding_ctor_explicit();
+ test_perfect_forwarding_ctor();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_perfect_forwarding_ctor_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_array.verify.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_array.verify.cpp
new file mode 100644
index 0000000000000..2e1a08a3b90bb
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_array.verify.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+#include <memory>
+
+#include "min_allocator.h"
+
+// Verify that std::indirect rejects array types.
+void test() {
+ // expected-error@*:* 2 {{static assertion failed}}
+ std::indirect<int[]> i1; // expected-note {{requested here}}
+ std::indirect<int[10]> i2; // expected-note {{requested here}}
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_cv_qualified.verify.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_cv_qualified.verify.cpp
new file mode 100644
index 0000000000000..a4138776a5754
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_cv_qualified.verify.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+#include <memory>
+
+#include "min_allocator.h"
+
+// Verify that std::indirect rejects cv-qualified types.
+void test() {
+ // Use bare_allocator, because std::allocator doesn't support cv-qualified types.
+ // expected-error@*:* 3 {{static assertion failed}}
+ std::indirect<const int, bare_allocator<const int>> i1; // expected-note {{requested here}}
+ std::indirect<volatile int, bare_allocator<volatile int>> i2; // expected-note {{requested here}}
+ std::indirect<const volatile int, bare_allocator<const volatile int>> i3; // expected-note {{requested here}}
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_t.verify.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_t.verify.cpp
new file mode 100644
index 0000000000000..5fa9e90540844
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_t.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+#include <memory>
+
+// Verify that std::indirect rejects in_place_t.
+void test() {
+ // expected-error@*:* 1 {{static assertion failed}}
+ std::indirect<std::in_place_t> i1; // expected-note {{requested here}}
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_type_t.verify.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_type_t.verify.cpp
new file mode 100644
index 0000000000000..348504c71abac
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_in_place_type_t.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+#include <memory>
+
+// Verify that std::indirect rejects specializations of in_place_type_t.
+void test() {
+ // expected-error@*:* 1 {{static assertion failed}}
+ std::indirect<std::in_place_type_t<int>> i1; // expected-note {{requested here}}
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_nonobject.verify.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_nonobject.verify.cpp
new file mode 100644
index 0000000000000..e0a0719ff7da1
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.general/indirect_nonobject.verify.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+#include <memory>
+
+#include "min_allocator.h"
+
+// Verify that std::indirect rejects nonobject types.
+void test() {
+ // expected-error@*:* 2 {{static assertion failed}}
+ std::indirect<void, bare_allocator<void>> i1; // expected-note {{requested here}}
+ std::indirect<int(), bare_allocator<int()>> i2; // expected-note {{requested here}}
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.hash/hash.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.hash/hash.pass.cpp
new file mode 100644
index 0000000000000..47bc9ad8fed2d
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.hash/hash.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class T, class Allocator>
+// struct hash<indirect<T, Allocator>>;
+
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+#include "test_convertible.h"
+
+constexpr bool test() {
+ { // Hashing an indirect hashes its owned object.
+ std::indirect<int> i1(1);
+ assert(std::hash<std::indirect<int>>()(i1) == std::hash<int>()(1));
+ }
+ { // Hashing a valueless indirect is valid and returns an implementation-defined value.
+ std::indirect<int> i1(1);
+ std::indirect<int> i2(2);
+ auto(std::move(i1));
+ auto(std::move(i2));
+ assert(std::hash<std::indirect<int>>()(i1) == std::hash<std::indirect<int>>()(i2));
+ }
+ { // hash<indirect<T>> is only enabled if hash<T> is.
+ static_assert(std::is_default_constructible_v<std::hash<std::indirect<int>>>);
+ struct S {};
+ static_assert(!std::is_default_constructible_v<std::hash<std::indirect<S>>>);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.obs/arrow.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.obs/arrow.pass.cpp
new file mode 100644
index 0000000000000..dfef1c629f7a3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.obs/arrow.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++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr const_pointer operator->() const noexcept;
+// constexpr pointer operator->() noexcept;
+
+#include <cassert>
+#include <memory>
+#include <utility>
+
+constexpr bool test() {
+ struct S {
+ constexpr bool is_const() & noexcept { return false; }
+ constexpr bool is_const() const& noexcept { return true; }
+ };
+
+ std::indirect<S> i;
+
+ assert(!i->is_const());
+ assert(std::as_const(i)->is_const());
+
+ static_assert(noexcept(i->is_const()));
+ static_assert(noexcept(std::as_const(i)->is_const()));
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp
new file mode 100644
index 0000000000000..b204be8845aee
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr const T& operator*() const & noexcept;
+// constexpr T& operator*() & noexcept;
+
+// constexpr const T&& operator*() const && noexcept;
+// constexpr T&& operator*() && noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <utility>
+
+constexpr bool test() {
+ std::indirect<int> i;
+
+ std::same_as<int&> decltype(auto) _ = *i;
+ std::same_as<int&&> decltype(auto) _ = *std::move(i);
+ std::same_as<const int&> decltype(auto) _ = *std::as_const(i);
+ std::same_as<const int&&> decltype(auto) _ = *std::move(std::as_const(i));
+
+ static_assert(noexcept(*i));
+ static_assert(noexcept(*std::move(i)));
+ static_assert(noexcept(*std::as_const(i)));
+ static_assert(noexcept(*std::move(std::as_const(i))));
+
+ struct Incomplete;
+ (void)([](std::indirect<Incomplete>& i) {
+ (void)(*i);
+ (void)(*std::move(i));
+ (void)(*std::as_const(i));
+ (void)(*std::move(std::as_const(i)));
+ });
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.pass.cpp
new file mode 100644
index 0000000000000..d0a6b9a87862b
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr allocator_type get_allocator() const noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <memory>
+
+constexpr bool test() {
+ const std::indirect<int> i;
+
+ std::same_as<std::allocator<int>> decltype(auto) _ = i.get_allocator();
+
+ static_assert(noexcept(i.get_allocator()));
+
+ struct Incomplete;
+ (void)([](std::indirect<Incomplete>& i) { return i.get_allocator(); });
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.pass.cpp
new file mode 100644
index 0000000000000..81943181681d5
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// constexpr bool valueless_after_move() const noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <memory>
+
+constexpr bool test() {
+ const std::indirect<int> i;
+
+ std::same_as<bool> decltype(auto) _ = i.valueless_after_move();
+
+ static_assert(noexcept(i.valueless_after_move()));
+
+ struct Incomplete;
+ (void)([](std::indirect<Incomplete>& i) { return i.valueless_after_move(); });
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.relops/eq.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.relops/eq.pass.cpp
new file mode 100644
index 0000000000000..450b9a1dae5c3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.relops/eq.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class U>
+// constexpr bool operator==(const indirect& lhs, const U& rhs) noexcept(noexcept(*lhs == rhs));
+
+#include <cassert>
+#include <memory>
+#include <utility>
+
+#include "test_comparisons.h"
+#include "test_allocator.h"
+
+constexpr bool test() {
+ { // Comparing indirects compares their held objects.
+ const std::indirect<int> i1;
+ const std::indirect<int> i2;
+ assert(testEquality(i1, i2, true));
+ static_assert(noexcept(i1 == i2));
+ static_assert(noexcept(i1 != i2));
+ }
+ { // Indirects can be compared even if they have different allocator types.
+ const std::indirect<int> i1;
+ const std::indirect<int, other_allocator<int>> i2;
+ assert(testEquality(i1, i2, true));
+ static_assert(noexcept(i1 == i2));
+ static_assert(noexcept(i1 != i2));
+ }
+ { // Valueless indirects always compare equal to each other and not equal to ones that hold values.
+ std::indirect<int> i1;
+ std::indirect<int> i2;
+ assert(testEquality(i1, i2, true));
+ auto(std::move(i1));
+ assert(testEquality(i1, i2, false));
+ auto(std::move(i2));
+ assert(testEquality(i1, i2, true));
+ }
+
+ return true;
+}
+
+void test_comparison_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct ComparisonThrows {
+ int i = 0;
+ bool operator==(ComparisonThrows) const { throw 42; }
+ };
+
+ std::indirect<ComparisonThrows> i1(1);
+ std::indirect<ComparisonThrows> i2(2);
+ static_assert(!noexcept(i1 == i2));
+ static_assert(!noexcept(i1 != i2));
+
+ try {
+ (void)(i1 == i2);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+
+ auto(std::move(i1));
+ assert(testEquality(i1, i2, false));
+ auto(std::move(i2));
+ assert(testEquality(i1, i2, true));
+#endif
+}
+
+int main(int, char**) {
+ test_comparison_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.relops/three_way.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.relops/three_way.pass.cpp
new file mode 100644
index 0000000000000..b5748d27bdfca
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.relops/three_way.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
+
+// <memory>
+
+// template <class T, class Allocator = std::allocator<T>> class indirect;
+
+// template<class U, class AA>
+// constexpr synth-three-way-result<T, U>
+// operator<=>(const indirect& lhs, const indirect<U, AA>& rhs);
+
+#include <cassert>
+#include <compare>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "test_comparisons.h"
+#include "test_allocator.h"
+
+constexpr bool test() {
+ { // Comparing indirects compares their held objects.
+ const std::indirect<int> i1;
+ const std::indirect<int> i2;
+ assert(testOrder(i1, i2, std::strong_ordering::equal));
+ }
+ { // Indirects can be compared even if they have different allocator types.
+ const std::indirect<int> i1(1);
+ const std::indirect<int, other_allocator<int>> i2(2);
+ assert(testOrder(i1, i2, std::strong_ordering::less));
+ }
+ { // Valueless indirects always compare equal to each other and less than ones that hold values.
+ std::indirect<int> i1;
+ std::indirect<int> i2(std::numeric_limits<int>::min());
+ auto(std::move(i1));
+ assert(testOrder(i1, i2, std::strong_ordering::less));
+ auto(std::move(i2));
+ assert(testOrder(i1, i2, std::strong_ordering::equal));
+ }
+
+ return true;
+}
+
+void test_comparison_throws() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ struct ComparisonThrows {
+ int i = 0;
+ bool operator==(ComparisonThrows) const { throw 42; }
+ std::strong_ordering operator<=>(ComparisonThrows) const { throw 42; }
+ };
+
+ std::indirect<ComparisonThrows> i1(1);
+ std::indirect<ComparisonThrows> i2(2);
+ try {
+ (void)(i1 <=> i2);
+ assert(false);
+ } catch (const int& e) {
+ assert(e == 42);
+ } catch (...) {
+ assert(false);
+ }
+
+ auto(std::move(i1));
+ assert(testOrder(i1, i2, std::strong_ordering::less));
+ auto(std::move(i2));
+ assert(testOrder(i1, i2, std::strong_ordering::equal));
+#endif
+}
+
+int main(int, char**) {
+ test_comparison_throws();
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.swap/swap.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.swap/swap.pass.cpp
new file mode 100644
index 0000000000000..0cd5b2fb6a726
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.swap/swap.pass.cpp
@@ -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
+
+// <memory>
+
+// constexpr void swap(indirect& other)
+// noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+// allocator_traits<Allocator>::is_always_equal::value);
+
+// constexpr void swap(indirect& lhs, indirect& rhs) noexcept(noexcept(lhs.swap(rhs)));
+
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+#include "test_allocator.h"
+#include "test_convertible.h"
+
+template <class T, bool POCS>
+struct pocs_allocator {
+ using value_type = T;
+ using propagate_on_container_swap = std::bool_constant<POCS>;
+
+ template <typename U>
+ struct rebind {
+ using other = pocs_allocator<U, POCS>;
+ };
+
+ int data = 0;
+
+ constexpr pocs_allocator(int i) : data(i) {}
+ constexpr T* allocate(size_t n) { return std::allocator<T>().allocate(n); }
+ constexpr void deallocate(T* ptr, size_t n) { return std::allocator<T>().deallocate(ptr, n); }
+
+ friend constexpr bool operator==(pocs_allocator, pocs_allocator) { return true; };
+
+ friend constexpr void swap(pocs_allocator& lhs, pocs_allocator& rhs) { std::swap(lhs.data, rhs.data); }
+};
+
+constexpr void test_swap_noexcept() {
+ std::indirect<int> i;
+ static_assert(noexcept(swap(i, i)));
+ static_assert(noexcept(i.swap(i)));
+}
+
+constexpr void test_swap() {
+ {
+ std::indirect<int> i1(1);
+ std::indirect<int> i2(2);
+ swap(i1, i2);
+ assert(*i1 == 2);
+ assert(*i2 == 1);
+ }
+ {
+ using A = pocs_allocator<int, true>;
+ std::indirect<int, A> i1(std::allocator_arg, A(1), 1);
+ std::indirect<int, A> i2(std::allocator_arg, A(2), 2);
+ swap(i1, i2);
+ assert(*i1 == 2);
+ assert(*i2 == 1);
+ assert(i1.get_allocator().data == 2);
+ assert(i2.get_allocator().data == 1);
+ static_assert(noexcept(swap(i1, i2)));
+ }
+ {
+ using A = pocs_allocator<int, false>;
+ std::indirect<int, A> i1(std::allocator_arg, A(1), 1);
+ std::indirect<int, A> i2(std::allocator_arg, A(2), 2);
+ swap(i1, i2);
+ assert(*i1 == 2);
+ assert(*i2 == 1);
+ assert(i1.get_allocator().data == 1);
+ assert(i2.get_allocator().data == 2);
+ static_assert(!noexcept(swap(i1, i2)));
+ }
+ struct Incomplete;
+ { // Swapping incomplete types is valid.
+ (void)([](std::indirect<Incomplete>& i) {
+ swap(i, i);
+ i.swap(i);
+ });
+ }
+}
+
+constexpr bool test() {
+ test_swap_noexcept();
+ test_swap();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h
index f8b622d7f9520..32c29969e20d2 100644
--- a/libcxx/test/support/test_allocator.h
+++ b/libcxx/test/support/test_allocator.h
@@ -498,17 +498,19 @@ struct SocccAllocator {
using value_type = T;
int count_ = 0;
- explicit SocccAllocator(int i) : count_(i) {}
+ TEST_CONSTEXPR_CXX20 explicit SocccAllocator(int i = 0) : count_(i) {}
template <class U>
- SocccAllocator(const SocccAllocator<U>& a) : count_(a.count_) {}
+ TEST_CONSTEXPR_CXX20 SocccAllocator(const SocccAllocator<U>& a) : count_(a.count_) {}
- T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
- void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); }
+ TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
+ TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); }
- SocccAllocator select_on_container_copy_construction() const { return SocccAllocator(count_ + 1); }
+ TEST_CONSTEXPR_CXX20 SocccAllocator select_on_container_copy_construction() const {
+ return SocccAllocator(count_ + 1);
+ }
- bool operator==(const SocccAllocator&) const { return true; }
+ TEST_CONSTEXPR_CXX20 bool operator==(const SocccAllocator&) const { return true; }
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
diff --git a/libcxx/test/support/test_convertible.h b/libcxx/test/support/test_convertible.h
index 805639716d91d..9eac0453cef06 100644
--- a/libcxx/test/support/test_convertible.h
+++ b/libcxx/test/support/test_convertible.h
@@ -14,6 +14,7 @@
// Unlike 'std::is_convertible' which only allows checking for single argument
// conversions.
+#include <type_traits>
#include <utility>
#include "test_macros.h"
@@ -38,4 +39,11 @@ template <class Tp, class ...Args>
constexpr bool test_convertible()
{ return detail::test_convertible_imp<Tp, Args...>(0); }
+#if TEST_STD_VER >= 20
+
+template <class Tp, class... Args>
+concept only_explicitly_constructible_from = std::is_constructible_v<Tp, Args...> && !test_convertible<Tp, Args...>();
+
+#endif // TEST_STD_VER >= 20
+
#endif // SUPPORT_TEST_CONVERTIBLE_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 22209f53d50d7..d0d7616a5825f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -746,6 +746,11 @@ def add_version_header(tc):
"values": {"c++17": 201505},
"headers": ["forward_list", "list", "vector"],
},
+ {
+ "name": "__cpp_lib_indirect",
+ "values": {"c++26": 202502}, # P3019R14 indirect and polymorphic: Vocabulary Types for Composite Class Design
+ "headers": ["memory"],
+ },
{
"name": "__cpp_lib_inplace_vector",
"values": {"c++26": 202406}, # P0843R14 inplace_vector
>From ce7ef100430aea35df1158f49f34c807141da367 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 06:49:21 +0000
Subject: [PATCH 2/8] python formatting
---
libcxx/utils/generate_feature_test_macro_components.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d0d7616a5825f..a9eadbdee22fc 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -748,7 +748,9 @@ def add_version_header(tc):
},
{
"name": "__cpp_lib_indirect",
- "values": {"c++26": 202502}, # P3019R14 indirect and polymorphic: Vocabulary Types for Composite Class Design
+ "values": {
+ "c++26": 202502
+ }, # P3019R14 indirect and polymorphic: Vocabulary Types for Composite Class Design
"headers": ["memory"],
},
{
>From b9d562638b4886c028b3a6d2fb3fd12efb2c93d1 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 07:23:05 +0000
Subject: [PATCH 3/8] Fix modules build
---
libcxx/include/module.modulemap.in | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 50a0cbde37a1e..2c0723a7b195c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1654,7 +1654,12 @@ module std [system] {
module destroy { header "__memory/destroy.h" }
module destruct_n { header "__memory/destruct_n.h" }
module fwd { header "__fwd/memory.h" }
- module indirect { header "__memory/indirect.h" }
+ module indirect {
+ header "__memory/indirect.h"
+ export std.memory.allocator
+ export std.compare.ordering
+ export std.utility.in_place
+ }
module inout_ptr { header "__memory/inout_ptr.h" }
module is_sufficiently_aligned { header "__memory/is_sufficiently_aligned.h" }
module noexcept_move_assign_container { header "__memory/noexcept_move_assign_container.h" }
>From e6b5699b10704e830e162a4b9e3b0839d8267aa6 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 08:08:04 +0000
Subject: [PATCH 4/8] Avoid reserved names, transitive include, and shadowing
---
libcxx/include/__memory/indirect.h | 81 ++++++++++---------
.../indirect/indirect.obs/deref.pass.cpp | 41 +++++-----
.../indirect.obs/get_allocator.pass.cpp | 15 ++--
.../valueless_after_move.pass.cpp | 15 ++--
4 files changed, 84 insertions(+), 68 deletions(-)
diff --git a/libcxx/include/__memory/indirect.h b/libcxx/include/__memory/indirect.h
index be55d2d0f18ef..7c3bbbe86fc77 100644
--- a/libcxx/include/__memory/indirect.h
+++ b/libcxx/include/__memory/indirect.h
@@ -22,21 +22,26 @@
#include <__memory/allocator_traits.h>
#include <__memory/swap_allocator.h>
#include <__type_traits/is_array.h>
+#include <__type_traits/is_assignable.h>
+#include <__type_traits/is_constructible.h>
#include <__type_traits/is_object.h>
#include <__type_traits/is_same.h>
#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
#include <__utility/exchange.h>
#include <__utility/forward.h>
#include <__utility/in_place.h>
#include <__utility/move.h>
#include <__utility/swap.h>
#include <initializer_list>
-#include <type_traits>
#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
@@ -92,17 +97,17 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
}
}
- template <class _U = _Tp>
- requires(!is_same_v<remove_cvref_t<_U>, indirect> && !is_same_v<remove_cvref_t<_U>, in_place_t> &&
- is_constructible_v<_Tp, _U> && is_default_constructible_v<_Allocator>)
- _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(_U&& __u)
- : __p_(__allocate_owned_object(__alloc_, std::forward<_U>(__u))) {}
+ template <class _Up = _Tp>
+ requires(!is_same_v<remove_cvref_t<_Up>, indirect> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+ is_constructible_v<_Tp, _Up> && is_default_constructible_v<_Allocator>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(_Up&& __u)
+ : __p_(__allocate_owned_object(__alloc_, std::forward<_Up>(__u))) {}
- template <class _U = _Tp>
- requires(!is_same_v<remove_cvref_t<_U>, indirect> && !is_same_v<remove_cvref_t<_U>, in_place_t> &&
- is_constructible_v<_Tp, _U>)
- _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, _U&& __u)
- : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_U>(__u))) {}
+ template <class _Up = _Tp>
+ requires(!is_same_v<remove_cvref_t<_Up>, indirect> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+ is_constructible_v<_Tp, _Up>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, _Up&& __u)
+ : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_Up>(__u))) {}
template <class... _Us>
requires(is_constructible_v<_Tp, _Us...> && is_default_constructible_v<_Allocator>)
@@ -114,15 +119,15 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, in_place_t, _Us&&... __us)
: __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {}
- template <class _I, class... _Us>
- requires(is_constructible_v<_Tp, initializer_list<_I>&, _Us...> && is_default_constructible_v<_Allocator>)
- _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, initializer_list<_I> __ilist, _Us&&... __us)
+ template <class _In, class... _Us>
+ requires(is_constructible_v<_Tp, initializer_list<_In>&, _Us...> && is_default_constructible_v<_Allocator>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, initializer_list<_In> __ilist, _Us&&... __us)
: __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
- template <class _I, class... _Us>
- requires is_constructible_v<_Tp, initializer_list<_I>&, _Us...>
+ template <class _In, class... _Us>
+ requires is_constructible_v<_Tp, initializer_list<_In>&, _Us...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(
- allocator_arg_t, const _Allocator& __a, in_place_t, initializer_list<_I> __ilist, _Us&&... __us)
+ allocator_arg_t, const _Allocator& __a, in_place_t, initializer_list<_In> __ilist, _Us&&... __us)
: __alloc_(__a), __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
// [indirect.dtor], destructor
@@ -189,13 +194,13 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
return *this;
}
- template <class _U = _Tp>
- requires(!is_same_v<remove_cvref_t<_U>, indirect> && is_constructible_v<_Tp, _U> && is_assignable_v<_Tp&, _U>)
- _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(_U&& __u) {
+ template <class _Up = _Tp>
+ requires(!is_same_v<remove_cvref_t<_Up>, indirect> && is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up>)
+ _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(_Up&& __u) {
if (valueless_after_move())
- __p_ = __allocate_owned_object(__alloc_, std::forward<_U>(__u));
+ __p_ = __allocate_owned_object(__alloc_, std::forward<_Up>(__u));
else
- *__p_ = std::forward<_U>(__u);
+ *__p_ = std::forward<_Up>(__u);
return *this;
}
@@ -257,31 +262,31 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
}
// [indirect.relops], relational operators
- template <class _U, class _AA>
+ template <class _Up, class _AA>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator==(const indirect& __lhs, const indirect<_U, _AA>& __rhs) noexcept(noexcept(*__lhs == *__rhs)) {
+ operator==(const indirect& __lhs, const indirect<_Up, _AA>& __rhs) noexcept(noexcept(*__lhs == *__rhs)) {
return (__lhs.valueless_after_move() == __rhs.valueless_after_move()) &&
(__lhs.valueless_after_move() || *__lhs == *__rhs);
}
- template <class _U, class _AA>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _U>
- operator<=>(const indirect& __lhs, const indirect<_U, _AA>& __rhs) {
+ template <class _Up, class _AA>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _Up>
+ operator<=>(const indirect& __lhs, const indirect<_Up, _AA>& __rhs) {
if (__lhs.valueless_after_move() || __rhs.valueless_after_move())
return !__lhs.valueless_after_move() <=> !__rhs.valueless_after_move();
return std::__synth_three_way(*__lhs, *__rhs);
}
// [indirect.comp.with.t], comparison with T
- template <class _U>
+ template <class _Up>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator==(const indirect& __lhs, const _U& __rhs) noexcept(noexcept(*__lhs == __rhs)) {
+ operator==(const indirect& __lhs, const _Up& __rhs) noexcept(noexcept(*__lhs == __rhs)) {
return !__lhs.valueless_after_move() && *__lhs == __rhs;
}
- template <class _U>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _U>
- operator<=>(const indirect& __lhs, const _U& __rhs) {
+ template <class _Up>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _Up>
+ operator<=>(const indirect& __lhs, const _Up& __rhs) {
return __lhs.valueless_after_move() ? strong_ordering::less : std::__synth_three_way(*__lhs, __rhs);
}
@@ -310,11 +315,11 @@ indirect(_Value) -> indirect<_Value>;
template <class _Allocator, class _Value>
indirect(allocator_arg_t, _Allocator, _Value) -> indirect<_Value, __rebind_alloc<allocator_traits<_Allocator>, _Value>>;
-template <class _T, class _Allocator>
- requires is_default_constructible_v<hash<_T>>
-struct hash<indirect<_T, _Allocator>> {
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const indirect<_T, _Allocator>& __i) const {
- return __i.valueless_after_move() ? 0 : hash<_T>()(*__i);
+template <class _Tp, class _Allocator>
+ requires is_default_constructible_v<hash<_Tp>>
+struct hash<indirect<_Tp, _Allocator>> {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const indirect<_Tp, _Allocator>& __i) const {
+ return __i.valueless_after_move() ? 0 : hash<_Tp>()(*__i);
}
};
@@ -329,4 +334,6 @@ _LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_STD_VER >= 26
+_LIBCPP_POP_MACROS
+
#endif // _LIBCPP___MEMORY_INDIRECT_H
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp
index b204be8845aee..1e690f46fac40 100644
--- a/libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.obs/deref.pass.cpp
@@ -24,25 +24,28 @@
#include <utility>
constexpr bool test() {
- std::indirect<int> i;
-
- std::same_as<int&> decltype(auto) _ = *i;
- std::same_as<int&&> decltype(auto) _ = *std::move(i);
- std::same_as<const int&> decltype(auto) _ = *std::as_const(i);
- std::same_as<const int&&> decltype(auto) _ = *std::move(std::as_const(i));
-
- static_assert(noexcept(*i));
- static_assert(noexcept(*std::move(i)));
- static_assert(noexcept(*std::as_const(i)));
- static_assert(noexcept(*std::move(std::as_const(i))));
-
- struct Incomplete;
- (void)([](std::indirect<Incomplete>& i) {
- (void)(*i);
- (void)(*std::move(i));
- (void)(*std::as_const(i));
- (void)(*std::move(std::as_const(i)));
- });
+ {
+ std::indirect<int> i;
+
+ std::same_as<int&> decltype(auto) _ = *i;
+ std::same_as<int&&> decltype(auto) _ = *std::move(i);
+ std::same_as<const int&> decltype(auto) _ = *std::as_const(i);
+ std::same_as<const int&&> decltype(auto) _ = *std::move(std::as_const(i));
+
+ static_assert(noexcept(*i));
+ static_assert(noexcept(*std::move(i)));
+ static_assert(noexcept(*std::as_const(i)));
+ static_assert(noexcept(*std::move(std::as_const(i))));
+ }
+ {
+ struct Incomplete;
+ (void)([](std::indirect<Incomplete>& i) {
+ (void)(*i);
+ (void)(*std::move(i));
+ (void)(*std::as_const(i));
+ (void)(*std::move(std::as_const(i)));
+ });
+ }
return true;
}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.pass.cpp
index d0a6b9a87862b..84c27c0b4695d 100644
--- a/libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.pass.cpp
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.obs/get_allocator.pass.cpp
@@ -19,14 +19,17 @@
#include <memory>
constexpr bool test() {
- const std::indirect<int> i;
+ {
+ const std::indirect<int> i;
- std::same_as<std::allocator<int>> decltype(auto) _ = i.get_allocator();
+ std::same_as<std::allocator<int>> decltype(auto) _ = i.get_allocator();
- static_assert(noexcept(i.get_allocator()));
-
- struct Incomplete;
- (void)([](std::indirect<Incomplete>& i) { return i.get_allocator(); });
+ static_assert(noexcept(i.get_allocator()));
+ }
+ {
+ struct Incomplete;
+ (void)([](std::indirect<Incomplete>& i) { return i.get_allocator(); });
+ }
return true;
}
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.pass.cpp
index 81943181681d5..6beadfe5c2864 100644
--- a/libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.obs/valueless_after_move.pass.cpp
@@ -19,14 +19,17 @@
#include <memory>
constexpr bool test() {
- const std::indirect<int> i;
+ {
+ const std::indirect<int> i;
- std::same_as<bool> decltype(auto) _ = i.valueless_after_move();
+ std::same_as<bool> decltype(auto) _ = i.valueless_after_move();
- static_assert(noexcept(i.valueless_after_move()));
-
- struct Incomplete;
- (void)([](std::indirect<Incomplete>& i) { return i.valueless_after_move(); });
+ static_assert(noexcept(i.valueless_after_move()));
+ }
+ {
+ struct Incomplete;
+ (void)([](std::indirect<Incomplete>& i) { return i.valueless_after_move(); });
+ }
return true;
}
>From b21fe5293fc266b3fdd2e4f1015d1ffc85025725 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 15:13:26 +0000
Subject: [PATCH 5/8] Add explicit include, temporarily disable some code
---
libcxx/include/__memory/indirect.h | 1 +
.../std/utilities/memory/indirect/indirect.ctor/move.pass.cpp | 3 +++
2 files changed, 4 insertions(+)
diff --git a/libcxx/include/__memory/indirect.h b/libcxx/include/__memory/indirect.h
index 7c3bbbe86fc77..fa0f6b1e23edc 100644
--- a/libcxx/include/__memory/indirect.h
+++ b/libcxx/include/__memory/indirect.h
@@ -12,6 +12,7 @@
#include <__config>
+#include <__assert>
#include <__compare/strong_order.h>
#include <__compare/synth_three_way.h>
#include <__functional/hash.h>
diff --git a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp
index a5d6059bf049c..d5d688905f27a 100644
--- a/libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/indirect/indirect.ctor/move.pass.cpp
@@ -86,6 +86,8 @@ constexpr void test_ctor() {
assert(i3.get_allocator().get_data() == 67);
assert(stats.construct_count == 1);
}
+// Temporary hack. Will need to fix before merging.
+#if 0
struct Incomplete;
{ // Move construction doesn't require T to be complete.
(void)([](std::indirect<Incomplete>&& i) -> std::indirect<Incomplete> { return {std::move(i)}; });
@@ -95,6 +97,7 @@ constexpr void test_ctor() {
return {std::allocator_arg, std::allocator<Incomplete>(), std::move(i)};
});
}
+#endif
}
constexpr bool test() {
>From 2cb37e7202859ffeafae92bd11094e39e48c897a Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 15:32:26 +0000
Subject: [PATCH 6/8] Add another module export
---
libcxx/include/module.modulemap.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 2c0723a7b195c..d93f4b4a133ca 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1659,6 +1659,7 @@ module std [system] {
export std.memory.allocator
export std.compare.ordering
export std.utility.in_place
+ export std.functional.hash
}
module inout_ptr { header "__memory/inout_ptr.h" }
module is_sufficiently_aligned { header "__memory/is_sufficiently_aligned.h" }
>From 9474baf652fb889a7afcbd805ea65a89a9e7d0c5 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 18:13:42 +0000
Subject: [PATCH 7/8] __p_ -> __ptr_
---
libcxx/include/__memory/indirect.h | 84 +++++++++++++++---------------
1 file changed, 42 insertions(+), 42 deletions(-)
diff --git a/libcxx/include/__memory/indirect.h b/libcxx/include/__memory/indirect.h
index fa0f6b1e23edc..ab16f64a53962 100644
--- a/libcxx/include/__memory/indirect.h
+++ b/libcxx/include/__memory/indirect.h
@@ -67,34 +67,34 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
// [indirect.ctor], constructors
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect()
requires is_default_constructible_v<_Allocator>
- : __p_(__allocate_owned_object(__alloc_)) {}
+ : __ptr_(__allocate_owned_object(__alloc_)) {}
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a)
- : __alloc_(__a), __p_(__allocate_owned_object(__alloc_)) {}
+ : __alloc_(__a), __ptr_(__allocate_owned_object(__alloc_)) {}
_LIBCPP_HIDE_FROM_ABI constexpr indirect(const indirect& __other)
: __alloc_(allocator_traits<_Allocator>::select_on_container_copy_construction(__other.__alloc_)),
- __p_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {}
+ __ptr_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {}
_LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, const indirect& __other)
- : __alloc_(__a), __p_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {}
+ : __alloc_(__a), __ptr_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {}
_LIBCPP_HIDE_FROM_ABI constexpr indirect(indirect&& __other) noexcept
- : __alloc_(std::move(__other.__alloc_)), __p_(std::exchange(__other.__p_, nullptr)) {}
+ : __alloc_(std::move(__other.__alloc_)), __ptr_(std::exchange(__other.__ptr_, nullptr)) {}
_LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, indirect&& __other) noexcept
requires allocator_traits<_Allocator>::is_always_equal::value
- : __alloc_(__a), __p_(std::exchange(__other.__p_, nullptr)) {}
+ : __alloc_(__a), __ptr_(std::exchange(__other.__ptr_, nullptr)) {}
_LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, indirect&& __other) : __alloc_(__a) {
if (__other.valueless_after_move()) {
- __p_ = nullptr;
+ __ptr_ = nullptr;
} else if (__alloc_ == __other.__alloc_) {
- __p_ = std::exchange(__other.__p_, nullptr);
+ __ptr_ = std::exchange(__other.__ptr_, nullptr);
} else {
- __p_ = __allocate_owned_object(__alloc_, *std::move(__other));
+ __ptr_ = __allocate_owned_object(__alloc_, *std::move(__other));
__other.__destroy_owned_object();
- __other.__p_ = nullptr;
+ __other.__ptr_ = nullptr;
}
}
@@ -102,34 +102,34 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
requires(!is_same_v<remove_cvref_t<_Up>, indirect> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
is_constructible_v<_Tp, _Up> && is_default_constructible_v<_Allocator>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(_Up&& __u)
- : __p_(__allocate_owned_object(__alloc_, std::forward<_Up>(__u))) {}
+ : __ptr_(__allocate_owned_object(__alloc_, std::forward<_Up>(__u))) {}
template <class _Up = _Tp>
requires(!is_same_v<remove_cvref_t<_Up>, indirect> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
is_constructible_v<_Tp, _Up>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, _Up&& __u)
- : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_Up>(__u))) {}
+ : __alloc_(__a), __ptr_(__allocate_owned_object(__alloc_, std::forward<_Up>(__u))) {}
template <class... _Us>
requires(is_constructible_v<_Tp, _Us...> && is_default_constructible_v<_Allocator>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, _Us&&... __us)
- : __p_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {}
+ : __ptr_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {}
template <class... _Us>
requires is_constructible_v<_Tp, _Us...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, in_place_t, _Us&&... __us)
- : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {}
+ : __alloc_(__a), __ptr_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {}
template <class _In, class... _Us>
requires(is_constructible_v<_Tp, initializer_list<_In>&, _Us...> && is_default_constructible_v<_Allocator>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, initializer_list<_In> __ilist, _Us&&... __us)
- : __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
+ : __ptr_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
template <class _In, class... _Us>
requires is_constructible_v<_Tp, initializer_list<_In>&, _Us...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(
allocator_arg_t, const _Allocator& __a, in_place_t, initializer_list<_In> __ilist, _Us&&... __us)
- : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
+ : __alloc_(__a), __ptr_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {}
// [indirect.dtor], destructor
_LIBCPP_HIDE_FROM_ABI constexpr ~indirect() { __destroy_owned_object(); }
@@ -143,20 +143,20 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
allocator_traits<_Allocator>::propagate_on_container_copy_assignment::value;
if (__other.valueless_after_move()) {
__destroy_owned_object();
- __p_ = nullptr;
+ __ptr_ = nullptr;
} else if (!valueless_after_move() && __alloc_ == __other.__alloc_) {
- *__p_ = *__other;
+ *__ptr_ = *__other;
} else {
- pointer __new_p;
+ pointer __new_ptr;
if constexpr (__propagate_allocator) {
// We need a mutable instance of the allocator, so make a copy.
_Allocator __alloc_copy = __other.__alloc_;
- __new_p = __allocate_owned_object(__alloc_copy, *__other);
+ __new_ptr = __allocate_owned_object(__alloc_copy, *__other);
} else {
- __new_p = __allocate_owned_object(__alloc_, *__other);
+ __new_ptr = __allocate_owned_object(__alloc_, *__other);
}
__destroy_owned_object();
- __p_ = __new_p;
+ __ptr_ = __new_ptr;
}
if constexpr (__propagate_allocator)
@@ -174,20 +174,20 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
static constexpr bool __propagate_allocator =
allocator_traits<_Allocator>::propagate_on_container_move_assignment::value;
- pointer __new_p;
+ pointer __new_ptr;
if constexpr (__propagate_allocator || allocator_traits<_Allocator>::is_always_equal::value) {
- __new_p = __other.__p_;
+ __new_ptr = __other.__ptr_;
} else if (__other.valueless_after_move()) {
- __new_p = nullptr;
+ __new_ptr = nullptr;
} else if (__alloc_ == __other.__alloc_) {
- __new_p = __other.__p_;
+ __new_ptr = __other.__ptr_;
} else {
- __new_p = __allocate_owned_object(__alloc_, *std::move(__other));
+ __new_ptr = __allocate_owned_object(__alloc_, *std::move(__other));
__other.__destroy_owned_object();
}
- __other.__p_ = nullptr;
+ __other.__ptr_ = nullptr;
__destroy_owned_object();
- __p_ = __new_p;
+ __ptr_ = __new_ptr;
if constexpr (__propagate_allocator)
__alloc_ = __other.__alloc_;
@@ -199,9 +199,9 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
requires(!is_same_v<remove_cvref_t<_Up>, indirect> && is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up>)
_LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(_Up&& __u) {
if (valueless_after_move())
- __p_ = __allocate_owned_object(__alloc_, std::forward<_Up>(__u));
+ __ptr_ = __allocate_owned_object(__alloc_, std::forward<_Up>(__u));
else
- *__p_ = std::forward<_Up>(__u);
+ *__ptr_ = std::forward<_Up>(__u);
return *this;
}
@@ -209,40 +209,40 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
!valueless_after_move(), "operator* called on a valueless std::indirect object");
- return *__p_;
+ return *__ptr_;
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
!valueless_after_move(), "operator* called on a valueless std::indirect object");
- return *__p_;
+ return *__ptr_;
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
!valueless_after_move(), "operator* called on a valueless std::indirect object");
- return std::move(*__p_);
+ return std::move(*__ptr_);
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
!valueless_after_move(), "operator* called on a valueless std::indirect object");
- return std::move(*__p_);
+ return std::move(*__ptr_);
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_pointer operator->() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
!valueless_after_move(), "operator-> called on a valueless std::indirect object");
- return __p_;
+ return __ptr_;
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr pointer operator->() noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
!valueless_after_move(), "operator-> called on a valueless std::indirect object");
- return __p_;
+ return __ptr_;
}
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool valueless_after_move() const noexcept { return !__p_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool valueless_after_move() const noexcept { return !__ptr_; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr allocator_type get_allocator() const noexcept { return __alloc_; }
@@ -253,7 +253,7 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(),
"swapping std::indirect objects with different allocators");
- std::swap(__p_, __other.__p_);
+ std::swap(__ptr_, __other.__ptr_);
std::__swap_allocator(__alloc_, __other.__alloc_);
}
@@ -301,13 +301,13 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
_LIBCPP_HIDE_FROM_ABI constexpr void __destroy_owned_object() noexcept {
if (!valueless_after_move()) {
- allocator_traits<_Allocator>::destroy(__alloc_, __p_);
- allocator_traits<_Allocator>::deallocate(__alloc_, __p_, 1);
+ allocator_traits<_Allocator>::destroy(__alloc_, __ptr_);
+ allocator_traits<_Allocator>::deallocate(__alloc_, __ptr_, 1);
}
}
_LIBCPP_NO_UNIQUE_ADDRESS _Allocator __alloc_ = _Allocator();
- pointer __p_;
+ pointer __ptr_;
};
template <class _Value>
>From 71e224f9946a537ed2a9b1bf9100f1089a020025 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 6 Nov 2025 21:03:07 +0000
Subject: [PATCH 8/8] Add error messages, deduplicate operator*
---
libcxx/include/__memory/indirect.h | 43 +++++++++++-------------------
1 file changed, 15 insertions(+), 28 deletions(-)
diff --git a/libcxx/include/__memory/indirect.h b/libcxx/include/__memory/indirect.h
index ab16f64a53962..39d86f8c96bfe 100644
--- a/libcxx/include/__memory/indirect.h
+++ b/libcxx/include/__memory/indirect.h
@@ -31,6 +31,7 @@
#include <__type_traits/remove_cvref.h>
#include <__utility/exchange.h>
#include <__utility/forward.h>
+#include <__utility/forward_like.h>
#include <__utility/in_place.h>
#include <__utility/move.h>
#include <__utility/swap.h>
@@ -56,13 +57,15 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
using const_pointer = allocator_traits<_Allocator>::const_pointer;
static_assert(__check_valid_allocator<allocator_type>::value);
- static_assert(is_same_v<typename allocator_type::value_type, value_type>);
- static_assert(is_object_v<value_type>);
- static_assert(!is_array_v<value_type>);
- static_assert(!is_same_v<value_type, in_place_t>);
- static_assert(!__is_inplace_type<value_type>::value);
+ static_assert(is_same_v<typename allocator_type::value_type, value_type>,
+ "allocator's value_type type must match std::indirect's held type");
+ static_assert(is_object_v<value_type>, "std::indirect cannot hold void or a reference or function type");
+ static_assert(!is_array_v<value_type>, "std::indirect cannot hold an array type");
+ static_assert(!is_same_v<value_type, in_place_t>, "std::indirect cannot hold std::in_place_t");
+ static_assert(!__is_inplace_type<value_type>::value,
+ "std::indirect cannot hold a specialization of std::in_place_type_t");
static_assert(std::is_same_v<value_type, remove_cv_t<value_type>>,
- "value_type must not be const or volatile qualified");
+ "std::indirect cannot hold a const or volatile qualified type");
// [indirect.ctor], constructors
_LIBCPP_HIDE_FROM_ABI constexpr explicit indirect()
@@ -206,28 +209,12 @@ class _LIBCPP_NO_SPECIALIZATIONS indirect {
}
// [indirect.obs], observers
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- !valueless_after_move(), "operator* called on a valueless std::indirect object");
- return *__ptr_;
- }
-
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- !valueless_after_move(), "operator* called on a valueless std::indirect object");
- return *__ptr_;
- }
-
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- !valueless_after_move(), "operator* called on a valueless std::indirect object");
- return std::move(*__ptr_);
- }
-
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- !valueless_after_move(), "operator* called on a valueless std::indirect object");
- return std::move(*__ptr_);
+ template <class _Self>
+ requires(!is_volatile_v<_Self>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto&& operator*(this _Self&& __self) noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS((!std::__forward_as<_Self, indirect>(__self).valueless_after_move()),
+ "operator* called on a valueless std::indirect object");
+ return std::forward_like<_Self>(*__self.__ptr_);
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_pointer operator->() const noexcept {
More information about the libcxx-commits
mailing list