[libcxx-commits] [libcxx] [libc++] Add an ABI setting to harden unique_ptr<T[]>::operator[] (PR #91798)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Aug 28 11:04:56 PDT 2024
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/91798
>From ab4a062ab96332e1ed0df6d0f4f80508777d88df Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 10 May 2024 15:40:18 -0400
Subject: [PATCH 1/3] [libc++] Harden unique_ptr<T[]>::operator[] when we can
This patch adds an ABI configuration that allows bounds-checking in
unique_ptr<T[]>::operator[] when it has been constructed with bounds
information in the API.
The patch also adds support for bounds-checking when an array cookie
is known to exist, which allows validating bounds without even changing
the ABI.
Drive-by changes:
- Improve the tests for `operator[]`
- Improve the tests for `.get()`
- Add a test for incomplete types support
---
...-hardening-mode-fast-with-abi-breaks.cmake | 2 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__configuration/abi.h | 7 +
libcxx/include/__memory/array_cookie.h | 55 ++++++++
libcxx/include/__memory/unique_ptr.h | 123 +++++++++++++++-
libcxx/include/module.modulemap | 1 +
.../unique.ptr.class/incomplete.sh.cpp | 93 ++++++++++++
.../assert.subscript.pass.cpp | 133 ++++++++++++++++++
.../unique.ptr.observers/get.pass.cpp | 117 ++++++++++++---
.../op_subscript.runtime.pass.cpp | 124 ++++++++++++----
10 files changed, 597 insertions(+), 59 deletions(-)
create mode 100644 libcxx/include/__memory/array_cookie.h
create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp
create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp
diff --git a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
index c0f2bad1c95af0..f63436c7679478 100644
--- a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
+++ b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
@@ -1,2 +1,2 @@
set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")
-set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR" CACHE STRING "")
+set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR;_LIBCPP_ABI_BOUNDED_UNIQUE_PTR" CACHE STRING "")
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8e..910501249c6786 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -528,6 +528,7 @@ set(files
__memory/allocator_arg_t.h
__memory/allocator_destructor.h
__memory/allocator_traits.h
+ __memory/array_cookie.h
__memory/assume_aligned.h
__memory/auto_ptr.h
__memory/builtin_new_allocator.h
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 8efbb42d1d8470..5078eaca97a79d 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -150,6 +150,13 @@
// ABI impact: changes the iterator type of `vector` (except `vector<bool>`).
// #define _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
+// Tracks the bounds of the array owned by std::unique_ptr<T[]>, allowing it to trap when accessed out-of-bounds.
+// Note that limited bounds checking is also available outside of this ABI configuration, but only some categories
+// of types can be checked.
+//
+// ABI impact: This causes the layout of std::unique_ptr<T[]> to change and its size to increase.
+// #define _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
+
#if defined(_LIBCPP_COMPILER_CLANG_BASED)
# if defined(__APPLE__)
# if defined(__i386__) || defined(__x86_64__)
diff --git a/libcxx/include/__memory/array_cookie.h b/libcxx/include/__memory/array_cookie.h
new file mode 100644
index 00000000000000..29f80df9e7c97a
--- /dev/null
+++ b/libcxx/include/__memory/array_cookie.h
@@ -0,0 +1,55 @@
+// -*- 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_ARRAY_COOKIE_H
+#define _LIBCPP___MEMORY_ARRAY_COOKIE_H
+
+#include <__config>
+
+#include <__configuration/abi.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__type_traits/negation.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// Trait representing whether a type requires an array cookie at the start of its allocation when
+// allocated as `new T[n]` and deallocated as `delete array`.
+//
+// Under the Itanium C++ ABI [1], we know that an array cookie is available unless `T` is trivially
+// destructible and the call to `operator delete[]` is not a sized operator delete. Under ABIs other
+// than the Itanium ABI, we assume there are no array cookies.
+//
+// [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies
+#ifdef _LIBCPP_ABI_ITANIUM
+// TODO: Use a builtin instead
+// TODO: We should factor in the choice of the usual deallocation function in this determination.
+template <class _Tp>
+struct __has_array_cookie : _Not<is_trivially_destructible<_Tp> > {};
+#else
+template <class _Tp>
+struct __has_array_cookie : false_type {};
+#endif
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI size_t __get_array_cookie(_Tp const* __ptr) {
+ static_assert(
+ __has_array_cookie<_Tp>::value, "Trying to access the array cookie of a type that is not guaranteed to have one");
+ size_t const* __cookie = reinterpret_cast<size_t const*>(__ptr) - 1; // TODO: Use a builtin instead
+ return *__cookie;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_ARRAY_COOKIE_H
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7f5e0ea243c956..ea2a38eab6f8f5 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -17,8 +17,10 @@
#include <__functional/hash.h>
#include <__functional/operations.h>
#include <__memory/allocator_traits.h> // __pointer
+#include <__memory/array_cookie.h>
#include <__memory/auto_ptr.h>
#include <__memory/compressed_pair.h>
+#include <__memory/pointer_traits.h>
#include <__type_traits/add_lvalue_reference.h>
#include <__type_traits/common_type.h>
#include <__type_traits/conditional.h>
@@ -40,6 +42,8 @@
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <__utility/move.h>
+#include <__utility/private_constructor_tag.h>
+#include <climits>
#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -283,6 +287,85 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& __u) _NOEXCEPT { __ptr_.swap(__u.__ptr_); }
};
+// Bounds checking in unique_ptr<T[]>
+// ==================================
+//
+// We provide some helper classes that allow bounds checking when accessing a unique_ptr<T[]>.
+// There are a few cases where bounds checking can be implemented:
+//
+// 1. When an array cookie (see [1]) exists at the beginning of the array allocation, we are
+// able to reuse that cookie to extract the size of the array and perform bounds checking.
+// An array cookie is a size inserted at the beginning of the allocation by the compiler.
+// That size is inserted implicitly when doing `new T[n]` in some cases, and its purpose
+// is to allow the runtime to destroy the `n` array elements when doing `delete array`.
+// When we are able to use array cookies, we reuse information already available in the
+// current runtime, so bounds checking does not require changing libc++'s ABI.
+//
+// 2. When the "bounded unique_ptr" ABI configuration (controlled by `_LIBCPP_ABI_BOUNDED_UNIQUE_PTR`)
+// is enabled, we store the size of the allocation (when it is known) so we can check it when
+// indexing into the `unique_ptr`. That changes the layout of `std::unique_ptr<T[]>`, which is
+// an ABI break from the default configuration.
+//
+// Note that even under this ABI configuration, we can't always know the size of the unique_ptr.
+// Indeed, the size of the allocation can only be known when the unique_ptr is created via
+// make_unique or a similar API. For example, it can't be known when constructed from an arbitrary
+// pointer, in which case we are not able to check the bounds on access:
+//
+// unique_ptr<T[], MyDeleter> ptr(new T[3]);
+//
+// When we don't know the size of the allocation via the API used to create the unique_ptr, we
+// try to fall back to using an array cookie when available.
+//
+// Finally, note that when this ABI configuration is enabled, we have no choice but to always
+// make space for a size to be stored in the unique_ptr. Indeed, while we might want to avoid
+// storing the size when an array cookie is available, knowing whether an array cookie is available
+// requires the type stored in the unique_ptr to be complete, while unique_ptr can normally
+// accommodate incomplete types.
+//
+// (1) Implementation where we rely on the array cookie to know the size of the allocation, if
+// an array cookie exists.
+struct __unique_ptr_array_bounds_stateless {
+ _LIBCPP_HIDE_FROM_ABI __unique_ptr_array_bounds_stateless() = default;
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __unique_ptr_array_bounds_stateless(size_t) {}
+
+ template <class _Tp, __enable_if_t<__has_array_cookie<_Tp>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp* __ptr, size_t __index) const {
+ size_t __cookie = std::__get_array_cookie(__ptr);
+ return __index < __cookie;
+ }
+
+ template <class _Tp, __enable_if_t<!__has_array_cookie<_Tp>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp*, size_t) const {
+ return true; // If we don't have an array cookie, we assume the access is in-bounds
+ }
+};
+
+// (2) Implementation where we store the size in the class whenever we have it.
+//
+// Semantically, we'd need to store the size as an optional<size_t>. However, since that
+// is really heavy weight, we instead store a size_t and use SIZE_MAX as a magic value
+// meaning that we don't know the size.
+struct __unique_ptr_array_bounds_stored {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __unique_ptr_array_bounds_stored() : __size_(SIZE_MAX) {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __unique_ptr_array_bounds_stored(size_t __size) : __size_(__size) {}
+
+ // Use the array cookie if there's one
+ template <class _Tp, __enable_if_t<__has_array_cookie<_Tp>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp* __ptr, size_t __index) const {
+ size_t __cookie = std::__get_array_cookie(__ptr);
+ return __index < __cookie;
+ }
+
+ // Otherwise, fall back on the stored size (if any)
+ template <class _Tp, __enable_if_t<!__has_array_cookie<_Tp>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp*, size_t __index) const {
+ return __index < __size_;
+ }
+
+private:
+ size_t __size_;
+};
+
template <class _Tp, class _Dp>
class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> {
public:
@@ -291,8 +374,9 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
typedef typename __pointer<_Tp, deleter_type>::type pointer;
// A unique_ptr contains the following members which may be trivially relocatable:
- // - pointer : this may be trivially relocatable, so it's checked
+ // - pointer: this may be trivially relocatable, so it's checked
// - deleter_type: this may be trivially relocatable, so it's checked
+ // - (optionally) size: this is trivially relocatable
//
// This unique_ptr implementation only contains a pointer to the unique object and a deleter, so there are no
// references to itself. This means that the entire structure is trivially relocatable if its members are.
@@ -303,6 +387,15 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
private:
__compressed_pair<pointer, deleter_type> __ptr_;
+#ifdef _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
+ using _BoundsChecker = __unique_ptr_array_bounds_stored;
+#else
+ using _BoundsChecker = __unique_ptr_array_bounds_stateless;
+#endif
+ _LIBCPP_NO_UNIQUE_ADDRESS _BoundsChecker __checker_;
+
+ template <class, class>
+ friend class unique_ptr; // access __checker_ in other unique_ptrs
template <class _From>
struct _CheckArrayPointerConversion : is_same<_From, pointer> {};
@@ -364,6 +457,12 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(_Pp __p) _NOEXCEPT : __ptr_(__p, __value_init_tag()) {}
+ // Private constructor used by make_unique & friends to pass the size that was allocated
+ template <class _Tag, class _Ptr, __enable_if_t<is_same<_Tag, __private_constructor_tag>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(_Tag, _Ptr __ptr, size_t __size) _NOEXCEPT
+ : __ptr_(__ptr, __value_init_tag()),
+ __checker_(__size) {}
+
template <class _Pp,
bool _Dummy = true,
class = _EnableIfDeleterConstructible<_LValRefType<_Dummy> >,
@@ -397,11 +496,13 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI unique_ptr(_Pp __p, _BadRValRefType<_Dummy> __d) = delete;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& __u) _NOEXCEPT
- : __ptr_(__u.release(), std::forward<deleter_type>(__u.get_deleter())) {}
+ : __ptr_(__u.release(), std::forward<deleter_type>(__u.get_deleter())),
+ __checker_(std::move(__u.__checker_)) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
reset(__u.release());
__ptr_.second() = std::forward<deleter_type>(__u.get_deleter());
+ __checker_ = std::move(__u.__checker_);
return *this;
}
@@ -410,7 +511,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _Up>,
class = _EnableIfDeleterConvertible<_Ep> >
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT
- : __ptr_(__u.release(), std::forward<_Ep>(__u.get_deleter())) {}
+ : __ptr_(__u.release(), std::forward<_Ep>(__u.get_deleter())),
+ __checker_(std::move(__u.__checker_)) {}
template <class _Up,
class _Ep,
@@ -419,6 +521,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT {
reset(__u.release());
__ptr_.second() = std::forward<_Ep>(__u.get_deleter());
+ __checker_ = std::move(__u.__checker_);
return *this;
}
@@ -436,6 +539,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __add_lvalue_reference_t<_Tp> operator[](size_t __i) const {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__checker_.__in_bounds(std::__to_address(__ptr_.first()), __i),
+ "unique_ptr<T[]>::operator[](index): index out of range");
return __ptr_.first()[__i];
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer get() const _NOEXCEPT { return __ptr_.first(); }
@@ -452,6 +557,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer release() _NOEXCEPT {
pointer __t = __ptr_.first();
__ptr_.first() = pointer();
+ __checker_ = _BoundsChecker();
return __t;
}
@@ -459,6 +565,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void reset(_Pp __p) _NOEXCEPT {
pointer __tmp = __ptr_.first();
__ptr_.first() = __p;
+ __checker_ = _BoundsChecker();
if (__tmp)
__ptr_.second()(__tmp);
}
@@ -466,11 +573,15 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void reset(nullptr_t = nullptr) _NOEXCEPT {
pointer __tmp = __ptr_.first();
__ptr_.first() = nullptr;
+ __checker_ = _BoundsChecker();
if (__tmp)
__ptr_.second()(__tmp);
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& __u) _NOEXCEPT { __ptr_.swap(__u.__ptr_); }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& __u) _NOEXCEPT {
+ __ptr_.swap(__u.__ptr_);
+ std::swap(__checker_, __u.__checker_);
+ }
};
template <class _Tp, class _Dp, __enable_if_t<__is_swappable_v<_Dp>, int> = 0>
@@ -626,7 +737,7 @@ template <class _Tp>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
make_unique(size_t __n) {
typedef __remove_extent_t<_Tp> _Up;
- return unique_ptr<_Tp>(new _Up[__n]());
+ return unique_ptr<_Tp>(__private_constructor_tag(), new _Up[__n](), __n);
}
template <class _Tp, class... _Args>
@@ -645,7 +756,7 @@ make_unique_for_overwrite() {
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
make_unique_for_overwrite(size_t __n) {
- return unique_ptr<_Tp>(new __remove_extent_t<_Tp>[__n]);
+ return unique_ptr<_Tp>(__private_constructor_tag(), new __remove_extent_t<_Tp>[__n], __n);
}
template <class _Tp, class... _Args>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 13d0dce34d97e3..581edefae293ac 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1509,6 +1509,7 @@ module std_private_memory_allocator [system] { header "__m
module std_private_memory_allocator_arg_t [system] { header "__memory/allocator_arg_t.h" }
module std_private_memory_allocator_destructor [system] { header "__memory/allocator_destructor.h" }
module std_private_memory_allocator_traits [system] { header "__memory/allocator_traits.h" }
+module std_private_memory_array_cookie [system] { header "__memory/array_cookie.h" }
module std_private_memory_assume_aligned [system] { header "__memory/assume_aligned.h" }
module std_private_memory_auto_ptr [system] { header "__memory/auto_ptr.h" }
module std_private_memory_builtin_new_allocator [system] {
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp
new file mode 100644
index 00000000000000..4a03d2bcf07bfe
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// Make sure that we can form unique_ptrs to incomplete types and perform restricted
+// operations on them. This requires setting up a TU where the type is complete and
+// the unique_ptr is created and destroyed, and a TU where the type is incomplete and
+// we check that a restricted set of operations can be performed on the unique_ptr.
+
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.tu1.o -DCOMPLETE
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.tu2.o -DINCOMPLETE
+// RUN: %{cxx} %t.tu1.o %t.tu2.o %{flags} %{link_flags} -o %t.exe
+// RUN: %{exec} %t.exe
+
+#include <memory>
+#include <cassert>
+
+struct T;
+extern void use(std::unique_ptr<T>& ptr);
+extern void use(std::unique_ptr<T[]>& ptr);
+
+#ifdef INCOMPLETE
+
+void use(std::unique_ptr<T>& ptr) {
+ {
+ T* x = ptr.get();
+ assert(x != nullptr);
+ }
+ {
+ T& ref = *ptr;
+ assert(&ref == ptr.get());
+ }
+ {
+ bool engaged = static_cast<bool>(ptr);
+ assert(engaged);
+ }
+ {
+ assert(ptr == ptr);
+ assert(!(ptr != ptr));
+ assert(!(ptr < ptr));
+ assert(!(ptr > ptr));
+ assert(ptr <= ptr);
+ assert(ptr >= ptr);
+ }
+}
+
+void use(std::unique_ptr<T[]>& ptr) {
+ {
+ T* x = ptr.get();
+ assert(x != nullptr);
+ }
+ {
+ bool engaged = static_cast<bool>(ptr);
+ assert(engaged);
+ }
+ {
+ assert(ptr == ptr);
+ assert(!(ptr != ptr));
+ assert(!(ptr < ptr));
+ assert(!(ptr > ptr));
+ assert(ptr <= ptr);
+ assert(ptr >= ptr);
+ }
+}
+
+#endif // INCOMPLETE
+
+#ifdef COMPLETE
+
+struct T {}; // complete the type
+
+int main(int, char**) {
+ {
+ std::unique_ptr<T> ptr(new T());
+ use(ptr);
+ }
+
+ {
+ std::unique_ptr<T[]> ptr(new T[3]());
+ use(ptr);
+ }
+ return 0;
+}
+
+#endif // COMPLETE
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp
new file mode 100644
index 00000000000000..6d5bacb94a2950
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp
@@ -0,0 +1,133 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <memory>
+//
+// unique_ptr<T[]>
+//
+// T& operator[](std::size_t);
+
+// This test ensures that we catch an out-of-bounds access in std::unique_ptr<T[]>::operator[]
+// when unique_ptr has the appropriate ABI configuration.
+
+#include <memory>
+#include <cstddef>
+#include <string>
+
+#include "check_assertion.h"
+
+struct MyDeleter {
+ void operator()(int* ptr) const { delete[] ptr; }
+};
+
+template <class WithCookie, class NoCookie>
+void test() {
+ // Check with a default deleter
+ {
+ // Types that have an array cookie
+ {
+ {
+ std::unique_ptr<WithCookie[]> ptr(new WithCookie[5]);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+ {
+ std::unique_ptr<WithCookie[]> ptr = std::make_unique<WithCookie[]>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+#if TEST_STD_VER >= 20
+ {
+ std::unique_ptr<WithCookie[]> ptr = std::make_unique_for_overwrite<WithCookie[]>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+#endif
+ }
+
+ // Types that don't have an array cookie (only available under the right ABI configuration)
+#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+ {
+ {
+ std::unique_ptr<NoCookie[]> ptr(new NoCookie[5]);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+ {
+ std::unique_ptr<NoCookie[]> ptr = std::make_unique<NoCookie[]>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+# if TEST_STD_VER >= 20
+ {
+ std::unique_ptr<NoCookie[]> ptr = std::make_unique_for_overwrite<NoCookie[]>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+# endif
+ }
+#endif // defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+ }
+
+ // Check with a custom deleter (only available under the right ABI configuration)
+#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+ {
+ // Types that have an array cookie
+ {
+ {
+ std::unique_ptr<WithCookie[], MyDeleter> ptr = std::make_unique<WithCookie[]>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+# if TEST_STD_VER >= 20
+ {
+ std::unique_ptr<WithCookie[], MyDeleter> ptr = std::make_unique_for_overwrite<WithCookie[], MyDeleter>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+# endif
+ }
+
+ // Types that don't have an array cookie
+ {
+ {
+ std::unique_ptr<NoCookie[], MyDeleter> ptr = std::make_unique<NoCookie[]>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+# if TEST_STD_VER >= 20
+ {
+ std::unique_ptr<NoCookie[], MyDeleter> ptr = std::make_unique_for_overwrite<NoCookie[], MyDeleter>(5);
+ TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
+ }
+# endif
+ }
+ }
+#endif // defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+}
+
+template <std::size_t Size>
+struct NoCookie {
+ char padding[Size];
+};
+
+template <std::size_t Size>
+struct WithCookie {
+ ~WithCookie() {}
+ char padding[Size];
+};
+
+int main(int, char**) {
+ test<WithCookie<1>, NoCookie<1>>();
+ test<WithCookie<2>, NoCookie<2>>();
+ test<WithCookie<3>, NoCookie<3>>();
+ test<WithCookie<4>, NoCookie<4>>();
+ test<WithCookie<8>, NoCookie<8>>();
+ test<WithCookie<16>, NoCookie<16>>();
+ test<WithCookie<32>, NoCookie<32>>();
+ test<WithCookie<256>, NoCookie<256>>();
+ test<std::string, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp
index 3bd3788960e2a6..75c35caf1b4d45 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp
@@ -10,43 +10,114 @@
// unique_ptr
-// test get
+// pointer unique_ptr<T>::get() const noexcept;
+// pointer unique_ptr<T[]>::get() const noexcept;
#include <memory>
#include <cassert>
+#include <cstddef>
#include "test_macros.h"
-#include "unique_ptr_test_helper.h"
-template <bool IsArray>
+template <class T>
TEST_CONSTEXPR_CXX23 void test_basic() {
- typedef typename std::conditional<IsArray, int[], int>::type VT;
- typedef const VT CVT;
+ // non-const element type
{
- typedef std::unique_ptr<VT> U;
- int* p = newValue<VT>(1);
- U s(p);
- U const& sc = s;
- ASSERT_SAME_TYPE(decltype(s.get()), int*);
- ASSERT_SAME_TYPE(decltype(sc.get()), int*);
- assert(s.get() == p);
- assert(sc.get() == s.get());
+ // non-const access
+ {
+ T* x = new T;
+ std::unique_ptr<T> ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+ ASSERT_NOEXCEPT(ptr.get());
+ assert(ptr.get() == x);
+ }
+
+ // const access
+ {
+ T* x = new T;
+ std::unique_ptr<T> const ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+ ASSERT_NOEXCEPT(ptr.get());
+ assert(ptr.get() == x);
+ }
+ }
+
+ // const element type
+ {
+ // non-const access
+ {
+ T* x = new T;
+ std::unique_ptr<T const> ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+ assert(ptr.get() == x);
+ }
+
+ // const access
+ {
+ T* x = new T;
+ std::unique_ptr<T const> const ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+ assert(ptr.get() == x);
+ }
+ }
+
+ // Same thing but for unique_ptr<T[]>
+ // non-const element type
+ {
+ // non-const access
+ {
+ T* x = new T[3];
+ std::unique_ptr<T[]> ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+ ASSERT_NOEXCEPT(ptr.get());
+ assert(ptr.get() == x);
+ }
+
+ // const access
+ {
+ T* x = new T[3];
+ std::unique_ptr<T[]> const ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+ ASSERT_NOEXCEPT(ptr.get());
+ assert(ptr.get() == x);
+ }
}
+
+ // const element type
{
- typedef std::unique_ptr<CVT> U;
- const int* p = newValue<VT>(1);
- U s(p);
- U const& sc = s;
- ASSERT_SAME_TYPE(decltype(s.get()), const int*);
- ASSERT_SAME_TYPE(decltype(sc.get()), const int*);
- assert(s.get() == p);
- assert(sc.get() == s.get());
+ // non-const access
+ {
+ T* x = new T[3];
+ std::unique_ptr<T const []> ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+ assert(ptr.get() == x);
+ }
+
+ // const access
+ {
+ T* x = new T[3];
+ std::unique_ptr<T const[]> const ptr(x);
+ ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+ assert(ptr.get() == x);
+ }
}
}
+template <std::size_t Size>
+struct WithSize {
+ char padding[Size];
+};
+
TEST_CONSTEXPR_CXX23 bool test() {
- test_basic</*IsArray*/ false>();
- test_basic<true>();
+ test_basic<char>();
+ test_basic<int>();
+ test_basic<WithSize<1> >();
+ test_basic<WithSize<2> >();
+ test_basic<WithSize<3> >();
+ test_basic<WithSize<4> >();
+ test_basic<WithSize<8> >();
+ test_basic<WithSize<16> >();
+ test_basic<WithSize<256> >();
return true;
}
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
index fbb4dbc6e03088..3e916d6b13d0f1 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
@@ -10,51 +10,117 @@
// unique_ptr
-// test op[](size_t)
+// T& unique_ptr::operator[](size_t) const
#include <memory>
#include <cassert>
-
-// TODO: Move TEST_IS_CONSTANT_EVALUATED into its own header
#include <type_traits>
+#include <array>
#include "test_macros.h"
+#include "type_algorithms.h"
-class A {
- int state_;
- static int next_;
+static int next = 0;
+struct EnumeratedDefaultCtor {
+ EnumeratedDefaultCtor() : value(0) { value = ++next; }
+ int value;
+};
-public:
- TEST_CONSTEXPR_CXX23 A() : state_(0) {
- if (!TEST_IS_CONSTANT_EVALUATED)
- state_ = ++next_;
+template <std::size_t Size>
+struct WithTrivialDtor {
+ std::array<char, Size> padding = {'x'};
+ TEST_CONSTEXPR_CXX23 friend bool operator==(WithTrivialDtor const& x, WithTrivialDtor const& y) {
+ return x.padding == y.padding;
}
+};
- TEST_CONSTEXPR_CXX23 int get() const { return state_; }
-
- friend TEST_CONSTEXPR_CXX23 bool operator==(const A& x, int y) { return x.state_ == y; }
-
- TEST_CONSTEXPR_CXX23 A& operator=(int i) {
- state_ = i;
- return *this;
+template <std::size_t Size>
+struct WithNonTrivialDtor {
+ std::array<char, Size> padding = {'x'};
+ TEST_CONSTEXPR_CXX23 friend bool operator==(WithNonTrivialDtor const& x, WithNonTrivialDtor const& y) {
+ return x.padding == y.padding;
}
+ ~WithNonTrivialDtor() {}
};
-int A::next_ = 0;
+template <class T>
+struct CustomDeleter : std::default_delete<T> {};
TEST_CONSTEXPR_CXX23 bool test() {
- std::unique_ptr<A[]> p(new A[3]);
- if (!TEST_IS_CONSTANT_EVALUATED) {
- assert(p[0] == 1);
- assert(p[1] == 2);
- assert(p[2] == 3);
+ // Basic test
+ {
+ std::unique_ptr<int[]> p(new int[3]);
+ {
+ int& result = p[0];
+ result = 0;
+ }
+ {
+ int& result = p[1];
+ result = 1;
+ }
+ {
+ int& result = p[2];
+ result = 2;
+ }
+
+ assert(p[0] == 0);
+ assert(p[1] == 1);
+ assert(p[2] == 2);
+ }
+
+ // Ensure that the order of access is correct after initializing a unique_ptr but
+ // before actually modifying any of its elements. The implementation would have to
+ // really try for this not to be the case, but we still check it.
+ //
+ // This requires assigning known values to the elements when they are first constructed,
+ // which requires global state.
+ {
+ if (!TEST_IS_CONSTANT_EVALUATED) {
+ std::unique_ptr<EnumeratedDefaultCtor[]> p(new EnumeratedDefaultCtor[3]);
+ assert(p[0].value == 1);
+ assert(p[1].value == 2);
+ assert(p[2].value == 3);
+ }
+ }
+
+ // Make sure operator[] is const-qualified
+ {
+ std::unique_ptr<int[]> const p(new int[3]);
+ p[0] = 42;
+ assert(p[0] == 42);
+ }
+
+ // Make sure we properly handle types with trivial and non-trivial destructors of different
+ // sizes. This is relevant because some implementations may want to use properties of the
+ // ABI like array cookies and these properties often depend on e.g. the triviality of T's
+ // destructor, T's size and so on.
+#if TEST_STD_VER >= 20 // this test is too painful to write before C++20
+ {
+ using TrickyCookieTypes = types::type_list<
+ WithTrivialDtor<1>,
+ WithTrivialDtor<2>,
+ WithTrivialDtor<3>,
+ WithTrivialDtor<4>,
+ WithTrivialDtor<8>,
+ WithTrivialDtor<16>,
+ WithTrivialDtor<256>,
+ WithNonTrivialDtor<1>,
+ WithNonTrivialDtor<2>,
+ WithNonTrivialDtor<3>,
+ WithNonTrivialDtor<4>,
+ WithNonTrivialDtor<8>,
+ WithNonTrivialDtor<16>,
+ WithNonTrivialDtor<256>>;
+ types::for_each(TrickyCookieTypes(), []<class T> {
+ types::for_each(types::type_list<std::default_delete<T[]>, CustomDeleter<T[]>>(), []<class Deleter> {
+ std::unique_ptr<T[], Deleter> p(new T[3]);
+ assert(p[0] == T());
+ assert(p[1] == T());
+ assert(p[2] == T());
+ });
+ });
}
- p[0] = 3;
- p[1] = 2;
- p[2] = 1;
- assert(p[0] == 3);
- assert(p[1] == 2);
- assert(p[2] == 1);
+#endif // C++20
return true;
}
>From ddf44b288da49bc63974b72f81e86d05fb2f498e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 28 Aug 2024 13:58:03 -0400
Subject: [PATCH 2/3] Add missing constexpr
---
.../unique.ptr.observers/op_subscript.runtime.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
index 3e916d6b13d0f1..ebfad8ec724e51 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
@@ -40,7 +40,7 @@ struct WithNonTrivialDtor {
TEST_CONSTEXPR_CXX23 friend bool operator==(WithNonTrivialDtor const& x, WithNonTrivialDtor const& y) {
return x.padding == y.padding;
}
- ~WithNonTrivialDtor() {}
+ TEST_CONSTEXPR_CXX23 ~WithNonTrivialDtor() {}
};
template <class T>
>From 0f56d47299971b1f5003efc3cc9f2e80a5cdc807 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 28 Aug 2024 14:04:27 -0400
Subject: [PATCH 3/3] Update transitive includes
---
.../test/libcxx/transitive_includes/cxx03.csv | 15 +++++++++++++
.../test/libcxx/transitive_includes/cxx11.csv | 16 ++++++++++++++
.../test/libcxx/transitive_includes/cxx14.csv | 16 ++++++++++++++
.../test/libcxx/transitive_includes/cxx17.csv | 16 ++++++++++++++
.../test/libcxx/transitive_includes/cxx20.csv | 16 ++++++++++++++
.../test/libcxx/transitive_includes/cxx23.csv | 22 +++++++++++++++++++
.../test/libcxx/transitive_includes/cxx26.csv | 22 +++++++++++++++++++
7 files changed, 123 insertions(+)
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index fd2cd7f1c2d961..a60ec8134a1012 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -20,6 +20,7 @@ algorithm utility
algorithm version
any atomic
any chrono
+any climits
any concepts
any cstddef
any cstdint
@@ -63,6 +64,7 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier climits
barrier concepts
barrier cstddef
barrier cstdint
@@ -139,6 +141,7 @@ cmath type_traits
cmath version
codecvt atomic
codecvt cctype
+codecvt climits
codecvt clocale
codecvt concepts
codecvt cstddef
@@ -204,6 +207,7 @@ cwchar cwctype
cwctype cctype
deque algorithm
deque atomic
+deque climits
deque compare
deque concepts
deque cstddef
@@ -310,6 +314,7 @@ forward_list typeinfo
forward_list version
fstream atomic
fstream cctype
+fstream climits
fstream clocale
fstream concepts
fstream cstddef
@@ -355,6 +360,7 @@ functional version
future atomic
future cerrno
future chrono
+future climits
future cstddef
future cstdint
future cstdlib
@@ -379,6 +385,7 @@ iomanip version
ios atomic
ios cctype
ios cerrno
+ios climits
ios clocale
ios concepts
ios cstddef
@@ -405,6 +412,7 @@ iostream streambuf
iostream version
istream bitset
istream cerrno
+istream climits
istream concepts
istream cstddef
istream cstdint
@@ -470,6 +478,7 @@ list version
locale atomic
locale cctype
locale cerrno
+locale climits
locale clocale
locale concepts
locale cstdarg
@@ -518,6 +527,7 @@ mdspan limits
mdspan span
mdspan version
memory atomic
+memory climits
memory compare
memory concepts
memory cstddef
@@ -607,6 +617,7 @@ optional version
ostream atomic
ostream bitset
ostream cerrno
+ostream climits
ostream concepts
ostream cstddef
ostream cstdint
@@ -682,6 +693,7 @@ ratio type_traits
ratio version
regex atomic
regex cctype
+regex climits
regex clocale
regex compare
regex concepts
@@ -769,6 +781,7 @@ span type_traits
span version
sstream bitset
sstream cerrno
+sstream climits
sstream cstddef
sstream cstdint
sstream cstring
@@ -936,6 +949,7 @@ typeinfo cstdlib
typeinfo type_traits
unordered_map algorithm
unordered_map bit
+unordered_map climits
unordered_map cmath
unordered_map compare
unordered_map concepts
@@ -952,6 +966,7 @@ unordered_map stdexcept
unordered_map tuple
unordered_map type_traits
unordered_map version
+unordered_set climits
unordered_set cmath
unordered_set compare
unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 04122fd0f4571a..8ab3bd163a037f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -20,6 +20,7 @@ algorithm utility
algorithm version
any atomic
any chrono
+any climits
any concepts
any cstddef
any cstdint
@@ -63,6 +64,7 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier climits
barrier concepts
barrier cstddef
barrier cstdint
@@ -139,6 +141,7 @@ cmath type_traits
cmath version
codecvt atomic
codecvt cctype
+codecvt climits
codecvt clocale
codecvt concepts
codecvt cstddef
@@ -205,6 +208,7 @@ cwchar cwctype
cwctype cctype
deque algorithm
deque atomic
+deque climits
deque compare
deque concepts
deque cstddef
@@ -311,6 +315,7 @@ forward_list typeinfo
forward_list version
fstream atomic
fstream cctype
+fstream climits
fstream clocale
fstream concepts
fstream cstddef
@@ -335,6 +340,7 @@ fstream typeinfo
fstream version
functional array
functional atomic
+functional climits
functional concepts
functional cstddef
functional cstdint
@@ -357,6 +363,7 @@ functional version
future atomic
future cerrno
future chrono
+future climits
future cstddef
future cstdint
future cstdlib
@@ -381,6 +388,7 @@ iomanip version
ios atomic
ios cctype
ios cerrno
+ios climits
ios clocale
ios concepts
ios cstddef
@@ -408,6 +416,7 @@ iostream streambuf
iostream version
istream bitset
istream cerrno
+istream climits
istream concepts
istream cstddef
istream cstdint
@@ -473,6 +482,7 @@ list version
locale atomic
locale cctype
locale cerrno
+locale climits
locale clocale
locale concepts
locale cstdarg
@@ -522,6 +532,7 @@ mdspan limits
mdspan span
mdspan version
memory atomic
+memory climits
memory compare
memory concepts
memory cstddef
@@ -612,6 +623,7 @@ optional version
ostream atomic
ostream bitset
ostream cerrno
+ostream climits
ostream concepts
ostream cstddef
ostream cstdint
@@ -687,6 +699,7 @@ ratio type_traits
ratio version
regex atomic
regex cctype
+regex climits
regex clocale
regex compare
regex concepts
@@ -775,6 +788,7 @@ span type_traits
span version
sstream bitset
sstream cerrno
+sstream climits
sstream cstddef
sstream cstdint
sstream cstring
@@ -943,6 +957,7 @@ typeinfo cstdlib
typeinfo type_traits
unordered_map algorithm
unordered_map bit
+unordered_map climits
unordered_map cmath
unordered_map compare
unordered_map concepts
@@ -959,6 +974,7 @@ unordered_map stdexcept
unordered_map tuple
unordered_map type_traits
unordered_map version
+unordered_set climits
unordered_set cmath
unordered_set compare
unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 42c742bead0c1f..59d65e16e4eeeb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -21,6 +21,7 @@ algorithm utility
algorithm version
any atomic
any chrono
+any climits
any concepts
any cstddef
any cstdint
@@ -64,6 +65,7 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier climits
barrier concepts
barrier cstddef
barrier cstdint
@@ -140,6 +142,7 @@ cmath type_traits
cmath version
codecvt atomic
codecvt cctype
+codecvt climits
codecvt clocale
codecvt concepts
codecvt cstddef
@@ -206,6 +209,7 @@ cwchar cwctype
cwctype cctype
deque algorithm
deque atomic
+deque climits
deque compare
deque concepts
deque cstddef
@@ -314,6 +318,7 @@ forward_list typeinfo
forward_list version
fstream atomic
fstream cctype
+fstream climits
fstream clocale
fstream concepts
fstream cstddef
@@ -338,6 +343,7 @@ fstream typeinfo
fstream version
functional array
functional atomic
+functional climits
functional concepts
functional cstddef
functional cstdint
@@ -360,6 +366,7 @@ functional version
future atomic
future cerrno
future chrono
+future climits
future cstddef
future cstdint
future cstdlib
@@ -384,6 +391,7 @@ iomanip version
ios atomic
ios cctype
ios cerrno
+ios climits
ios clocale
ios concepts
ios cstddef
@@ -411,6 +419,7 @@ iostream streambuf
iostream version
istream bitset
istream cerrno
+istream climits
istream concepts
istream cstddef
istream cstdint
@@ -476,6 +485,7 @@ list version
locale atomic
locale cctype
locale cerrno
+locale climits
locale clocale
locale concepts
locale cstdarg
@@ -525,6 +535,7 @@ mdspan limits
mdspan span
mdspan version
memory atomic
+memory climits
memory compare
memory concepts
memory cstddef
@@ -615,6 +626,7 @@ optional version
ostream atomic
ostream bitset
ostream cerrno
+ostream climits
ostream concepts
ostream cstddef
ostream cstdint
@@ -690,6 +702,7 @@ ratio type_traits
ratio version
regex atomic
regex cctype
+regex climits
regex clocale
regex compare
regex concepts
@@ -778,6 +791,7 @@ span type_traits
span version
sstream bitset
sstream cerrno
+sstream climits
sstream cstddef
sstream cstdint
sstream cstring
@@ -946,6 +960,7 @@ typeinfo cstdlib
typeinfo type_traits
unordered_map algorithm
unordered_map bit
+unordered_map climits
unordered_map cmath
unordered_map compare
unordered_map concepts
@@ -962,6 +977,7 @@ unordered_map stdexcept
unordered_map tuple
unordered_map type_traits
unordered_map version
+unordered_set climits
unordered_set cmath
unordered_set compare
unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index bc0659127d4bf5..0c4328081f6750 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -21,6 +21,7 @@ algorithm utility
algorithm version
any atomic
any chrono
+any climits
any concepts
any cstddef
any cstdint
@@ -64,6 +65,7 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier climits
barrier concepts
barrier cstddef
barrier cstdint
@@ -140,6 +142,7 @@ cmath type_traits
cmath version
codecvt atomic
codecvt cctype
+codecvt climits
codecvt clocale
codecvt concepts
codecvt cstddef
@@ -206,6 +209,7 @@ cwchar cwctype
cwctype cctype
deque algorithm
deque atomic
+deque climits
deque compare
deque concepts
deque cstddef
@@ -314,6 +318,7 @@ forward_list typeinfo
forward_list version
fstream atomic
fstream cctype
+fstream climits
fstream clocale
fstream concepts
fstream cstddef
@@ -338,6 +343,7 @@ fstream typeinfo
fstream version
functional array
functional atomic
+functional climits
functional concepts
functional cstddef
functional cstdint
@@ -360,6 +366,7 @@ functional version
future atomic
future cerrno
future chrono
+future climits
future cstddef
future cstdint
future cstdlib
@@ -384,6 +391,7 @@ iomanip version
ios atomic
ios cctype
ios cerrno
+ios climits
ios clocale
ios concepts
ios cstddef
@@ -411,6 +419,7 @@ iostream streambuf
iostream version
istream bitset
istream cerrno
+istream climits
istream concepts
istream cstddef
istream cstdint
@@ -476,6 +485,7 @@ list version
locale atomic
locale cctype
locale cerrno
+locale climits
locale clocale
locale concepts
locale cstdarg
@@ -525,6 +535,7 @@ mdspan limits
mdspan span
mdspan version
memory atomic
+memory climits
memory compare
memory concepts
memory cstddef
@@ -616,6 +627,7 @@ optional version
ostream atomic
ostream bitset
ostream cerrno
+ostream climits
ostream concepts
ostream cstddef
ostream cstdint
@@ -691,6 +703,7 @@ ratio type_traits
ratio version
regex atomic
regex cctype
+regex climits
regex clocale
regex compare
regex concepts
@@ -779,6 +792,7 @@ span type_traits
span version
sstream bitset
sstream cerrno
+sstream climits
sstream cstddef
sstream cstdint
sstream cstring
@@ -947,6 +961,7 @@ typeinfo cstdlib
typeinfo type_traits
unordered_map algorithm
unordered_map bit
+unordered_map climits
unordered_map cmath
unordered_map compare
unordered_map concepts
@@ -963,6 +978,7 @@ unordered_map stdexcept
unordered_map tuple
unordered_map type_traits
unordered_map version
+unordered_set climits
unordered_set cmath
unordered_set compare
unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index fed0944f0219c4..bfb28bbcdbb749 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -20,6 +20,7 @@ algorithm type_traits
algorithm utility
algorithm version
any atomic
+any climits
any concepts
any cstddef
any cstdint
@@ -63,6 +64,7 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier climits
barrier concepts
barrier cstddef
barrier cstdint
@@ -145,6 +147,7 @@ cmath type_traits
cmath version
codecvt atomic
codecvt cctype
+codecvt climits
codecvt clocale
codecvt concepts
codecvt cstddef
@@ -214,6 +217,7 @@ cwchar cwctype
cwctype cctype
deque algorithm
deque atomic
+deque climits
deque compare
deque concepts
deque cstddef
@@ -322,6 +326,7 @@ forward_list typeinfo
forward_list version
fstream atomic
fstream cctype
+fstream climits
fstream clocale
fstream concepts
fstream cstddef
@@ -346,6 +351,7 @@ fstream typeinfo
fstream version
functional array
functional atomic
+functional climits
functional concepts
functional cstddef
functional cstdint
@@ -367,6 +373,7 @@ functional vector
functional version
future atomic
future cerrno
+future climits
future cstddef
future cstdint
future cstdlib
@@ -391,6 +398,7 @@ iomanip version
ios atomic
ios cctype
ios cerrno
+ios climits
ios clocale
ios concepts
ios cstddef
@@ -418,6 +426,7 @@ iostream streambuf
iostream version
istream bitset
istream cerrno
+istream climits
istream concepts
istream cstddef
istream cstdint
@@ -483,6 +492,7 @@ list version
locale atomic
locale cctype
locale cerrno
+locale climits
locale clocale
locale concepts
locale cstdarg
@@ -532,6 +542,7 @@ mdspan limits
mdspan span
mdspan version
memory atomic
+memory climits
memory compare
memory concepts
memory cstddef
@@ -623,6 +634,7 @@ optional version
ostream atomic
ostream bitset
ostream cerrno
+ostream climits
ostream concepts
ostream cstddef
ostream cstdint
@@ -699,6 +711,7 @@ ratio type_traits
ratio version
regex atomic
regex cctype
+regex climits
regex clocale
regex compare
regex concepts
@@ -787,6 +800,7 @@ span type_traits
span version
sstream bitset
sstream cerrno
+sstream climits
sstream cstddef
sstream cstdint
sstream cstring
@@ -954,6 +968,7 @@ typeinfo cstdlib
typeinfo type_traits
unordered_map algorithm
unordered_map bit
+unordered_map climits
unordered_map cmath
unordered_map compare
unordered_map concepts
@@ -970,6 +985,7 @@ unordered_map stdexcept
unordered_map tuple
unordered_map type_traits
unordered_map version
+unordered_set climits
unordered_set cmath
unordered_set compare
unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 53f99384a7f573..2ebec7dd0f0439 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -12,6 +12,7 @@ algorithm optional
algorithm ratio
algorithm tuple
algorithm version
+any climits
any cstddef
any cstdint
any cstring
@@ -36,6 +37,7 @@ atomic ctime
atomic limits
atomic ratio
atomic version
+barrier climits
barrier cstddef
barrier cstdint
barrier cstring
@@ -90,6 +92,7 @@ cmath initializer_list
cmath limits
cmath version
codecvt cctype
+codecvt climits
codecvt clocale
codecvt cstddef
codecvt cstdint
@@ -115,6 +118,7 @@ concepts cstddef
concepts version
condition_variable atomic
condition_variable cerrno
+condition_variable climits
condition_variable cstddef
condition_variable cstdint
condition_variable cstring
@@ -136,6 +140,7 @@ ctgmath ccomplex
ctgmath cmath
cwchar cwctype
cwctype cctype
+deque climits
deque compare
deque cstddef
deque cstdint
@@ -184,6 +189,7 @@ filesystem version
format array
format cctype
format cerrno
+format climits
format clocale
format cmath
format cstddef
@@ -211,6 +217,7 @@ forward_list new
forward_list tuple
forward_list version
fstream cctype
+fstream climits
fstream clocale
fstream cstddef
fstream cstdint
@@ -229,6 +236,7 @@ fstream tuple
fstream typeinfo
fstream version
functional array
+functional climits
functional cstddef
functional cstdint
functional cstring
@@ -241,6 +249,7 @@ functional unordered_map
functional vector
functional version
future cerrno
+future climits
future cstddef
future cstdint
future cstdlib
@@ -260,6 +269,7 @@ iomanip istream
iomanip version
ios cctype
ios cerrno
+ios climits
ios clocale
ios cstddef
ios cstdint
@@ -285,6 +295,7 @@ iostream streambuf
iostream version
istream bitset
istream cerrno
+istream climits
istream cstddef
istream cstdint
istream cstring
@@ -326,6 +337,7 @@ list tuple
list version
locale cctype
locale cerrno
+locale climits
locale clocale
locale cstddef
locale cstdint
@@ -344,6 +356,7 @@ locale string
locale tuple
locale typeinfo
locale version
+map climits
map compare
map cstddef
map cstdint
@@ -362,6 +375,7 @@ mdspan cstddef
mdspan limits
mdspan span
mdspan version
+memory climits
memory compare
memory cstddef
memory cstdint
@@ -380,6 +394,7 @@ memory_resource new
memory_resource tuple
memory_resource version
mutex cerrno
+mutex climits
mutex cstddef
mutex cstdint
mutex ctime
@@ -415,6 +430,7 @@ optional new
optional version
ostream bitset
ostream cerrno
+ostream climits
ostream cstddef
ostream cstdint
ostream cstring
@@ -473,6 +489,7 @@ ratio climits
ratio cstdint
ratio version
regex cctype
+regex climits
regex clocale
regex compare
regex cstddef
@@ -503,6 +520,7 @@ semaphore ctime
semaphore limits
semaphore ratio
semaphore version
+set climits
set compare
set cstddef
set cstdint
@@ -530,6 +548,7 @@ span stdexcept
span version
sstream bitset
sstream cerrno
+sstream climits
sstream cstddef
sstream cstdint
sstream cstring
@@ -625,6 +644,7 @@ thread array
thread atomic
thread cctype
thread cerrno
+thread climits
thread clocale
thread compare
thread cstddef
@@ -658,6 +678,7 @@ typeindex typeinfo
typeindex version
typeinfo cstddef
typeinfo cstdint
+unordered_map climits
unordered_map compare
unordered_map cstddef
unordered_map cstdint
@@ -669,6 +690,7 @@ unordered_map optional
unordered_map stdexcept
unordered_map tuple
unordered_map version
+unordered_set climits
unordered_set compare
unordered_set cstddef
unordered_set cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 53f99384a7f573..2ebec7dd0f0439 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -12,6 +12,7 @@ algorithm optional
algorithm ratio
algorithm tuple
algorithm version
+any climits
any cstddef
any cstdint
any cstring
@@ -36,6 +37,7 @@ atomic ctime
atomic limits
atomic ratio
atomic version
+barrier climits
barrier cstddef
barrier cstdint
barrier cstring
@@ -90,6 +92,7 @@ cmath initializer_list
cmath limits
cmath version
codecvt cctype
+codecvt climits
codecvt clocale
codecvt cstddef
codecvt cstdint
@@ -115,6 +118,7 @@ concepts cstddef
concepts version
condition_variable atomic
condition_variable cerrno
+condition_variable climits
condition_variable cstddef
condition_variable cstdint
condition_variable cstring
@@ -136,6 +140,7 @@ ctgmath ccomplex
ctgmath cmath
cwchar cwctype
cwctype cctype
+deque climits
deque compare
deque cstddef
deque cstdint
@@ -184,6 +189,7 @@ filesystem version
format array
format cctype
format cerrno
+format climits
format clocale
format cmath
format cstddef
@@ -211,6 +217,7 @@ forward_list new
forward_list tuple
forward_list version
fstream cctype
+fstream climits
fstream clocale
fstream cstddef
fstream cstdint
@@ -229,6 +236,7 @@ fstream tuple
fstream typeinfo
fstream version
functional array
+functional climits
functional cstddef
functional cstdint
functional cstring
@@ -241,6 +249,7 @@ functional unordered_map
functional vector
functional version
future cerrno
+future climits
future cstddef
future cstdint
future cstdlib
@@ -260,6 +269,7 @@ iomanip istream
iomanip version
ios cctype
ios cerrno
+ios climits
ios clocale
ios cstddef
ios cstdint
@@ -285,6 +295,7 @@ iostream streambuf
iostream version
istream bitset
istream cerrno
+istream climits
istream cstddef
istream cstdint
istream cstring
@@ -326,6 +337,7 @@ list tuple
list version
locale cctype
locale cerrno
+locale climits
locale clocale
locale cstddef
locale cstdint
@@ -344,6 +356,7 @@ locale string
locale tuple
locale typeinfo
locale version
+map climits
map compare
map cstddef
map cstdint
@@ -362,6 +375,7 @@ mdspan cstddef
mdspan limits
mdspan span
mdspan version
+memory climits
memory compare
memory cstddef
memory cstdint
@@ -380,6 +394,7 @@ memory_resource new
memory_resource tuple
memory_resource version
mutex cerrno
+mutex climits
mutex cstddef
mutex cstdint
mutex ctime
@@ -415,6 +430,7 @@ optional new
optional version
ostream bitset
ostream cerrno
+ostream climits
ostream cstddef
ostream cstdint
ostream cstring
@@ -473,6 +489,7 @@ ratio climits
ratio cstdint
ratio version
regex cctype
+regex climits
regex clocale
regex compare
regex cstddef
@@ -503,6 +520,7 @@ semaphore ctime
semaphore limits
semaphore ratio
semaphore version
+set climits
set compare
set cstddef
set cstdint
@@ -530,6 +548,7 @@ span stdexcept
span version
sstream bitset
sstream cerrno
+sstream climits
sstream cstddef
sstream cstdint
sstream cstring
@@ -625,6 +644,7 @@ thread array
thread atomic
thread cctype
thread cerrno
+thread climits
thread clocale
thread compare
thread cstddef
@@ -658,6 +678,7 @@ typeindex typeinfo
typeindex version
typeinfo cstddef
typeinfo cstdint
+unordered_map climits
unordered_map compare
unordered_map cstddef
unordered_map cstdint
@@ -669,6 +690,7 @@ unordered_map optional
unordered_map stdexcept
unordered_map tuple
unordered_map version
+unordered_set climits
unordered_set compare
unordered_set cstddef
unordered_set cstdint
More information about the libcxx-commits
mailing list