diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 0277382cc84dd..8cb05857ea8d7 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -845,6 +845,7 @@ set(files __type_traits/is_reference.h __type_traits/is_reference_wrapper.h __type_traits/is_referenceable.h + __type_traits/is_replaceable.h __type_traits/is_same.h __type_traits/is_scalar.h __type_traits/is_signed.h diff --git a/libcxx/include/__exception/exception_ptr.h b/libcxx/include/__exception/exception_ptr.h index dac5b00b57fe3..b1fe9a1299ec7 100644 --- a/libcxx/include/__exception/exception_ptr.h +++ b/libcxx/include/__exception/exception_ptr.h @@ -65,8 +65,10 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr { friend _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep) _NOEXCEPT; public: - // exception_ptr is basically a COW string. + // exception_ptr is basically a COW string so it is trivially relocatable. + // It is also replaceable because assignment has normal value semantics. using __trivially_relocatable _LIBCPP_NODEBUG = exception_ptr; + using __replaceable _LIBCPP_NODEBUG = exception_ptr; _LIBCPP_HIDE_FROM_ABI exception_ptr() _NOEXCEPT : __ptr_() {} _LIBCPP_HIDE_FROM_ABI exception_ptr(nullptr_t) _NOEXCEPT : __ptr_() {} diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h index 03bbd1623ed5c..a1971cbb36a26 100644 --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -29,6 +29,7 @@ #include <__type_traits/is_nothrow_assignable.h> #include <__type_traits/is_nothrow_constructible.h> #include <__type_traits/is_reference.h> +#include <__type_traits/is_replaceable.h> #include <__type_traits/is_same.h> #include <__type_traits/is_swappable.h> #include <__type_traits/is_trivially_constructible.h> @@ -470,6 +471,8 @@ class expected : private __expected_base<_Tp, _Err> { __conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value && __libcpp_is_trivially_relocatable<_Err>::value, expected, void>; + using __replaceable _LIBCPP_NODEBUG = + __conditional_t<__is_replaceable_v<_Tp> && __is_replaceable_v<_Err>, expected, void>; template using rebind = expected<_Up, error_type>; diff --git a/libcxx/include/__locale b/libcxx/include/__locale index 3c290e27c062b..92e45e2531c2a 100644 --- a/libcxx/include/__locale +++ b/libcxx/include/__locale @@ -56,8 +56,10 @@ _LIBCPP_HIDE_FROM_ABI const _Facet& use_facet(const locale&); class _LIBCPP_EXPORTED_FROM_ABI locale { public: - // locale is essentially a shared_ptr that doesn't support weak_ptrs and never got a move constructor. + // locale is essentially a shared_ptr that doesn't support weak_ptrs and never got a move constructor, + // so it is trivially relocatable. Like shared_ptr, it is also replaceable. using __trivially_relocatable _LIBCPP_NODEBUG = locale; + using __replaceable _LIBCPP_NODEBUG = locale; // types: class _LIBCPP_EXPORTED_FROM_ABI facet; diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h index 6d3da9778f5f3..f1b2e2dbfc0cd 100644 --- a/libcxx/include/__memory/shared_ptr.h +++ b/libcxx/include/__memory/shared_ptr.h @@ -316,8 +316,10 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr { #endif // A shared_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require - // any bookkeeping, so it's always trivially relocatable. + // any bookkeeping, so it's always trivially relocatable. It is also replaceable because assignment just rebinds the + // shared_ptr to manage a different object. using __trivially_relocatable _LIBCPP_NODEBUG = shared_ptr; + using __replaceable _LIBCPP_NODEBUG = shared_ptr; private: element_type* __ptr_; @@ -1211,8 +1213,9 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI weak_ptr { #endif // A weak_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require - // any bookkeeping, so it's always trivially relocatable. + // any bookkeeping, so it's always trivially relocatable. It's also replaceable for the same reason. using __trivially_relocatable _LIBCPP_NODEBUG = weak_ptr; + using __replaceable _LIBCPP_NODEBUG = weak_ptr; private: element_type* __ptr_; diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h index fa02926bd5e5a..3e8d3cd6a4097 100644 --- a/libcxx/include/__memory/unique_ptr.h +++ b/libcxx/include/__memory/unique_ptr.h @@ -39,6 +39,7 @@ #include <__type_traits/is_function.h> #include <__type_traits/is_pointer.h> #include <__type_traits/is_reference.h> +#include <__type_traits/is_replaceable.h> #include <__type_traits/is_same.h> #include <__type_traits/is_swappable.h> #include <__type_traits/is_trivially_relocatable.h> @@ -144,6 +145,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr { __libcpp_is_trivially_relocatable::value && __libcpp_is_trivially_relocatable::value, unique_ptr, void>; + using __replaceable _LIBCPP_NODEBUG = + __conditional_t<__is_replaceable_v && __is_replaceable_v, unique_ptr, void>; private: _LIBCPP_COMPRESSED_PAIR(pointer, __ptr_, deleter_type, __deleter_); @@ -410,6 +413,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr<_Tp[], _Dp> { __libcpp_is_trivially_relocatable::value && __libcpp_is_trivially_relocatable::value, unique_ptr, void>; + using __replaceable _LIBCPP_NODEBUG = + __conditional_t<__is_replaceable_v && __is_replaceable_v, unique_ptr, void>; private: template diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer index 721d4d497f2a5..21e58f4abc6b3 100644 --- a/libcxx/include/__split_buffer +++ b/libcxx/include/__split_buffer @@ -28,6 +28,7 @@ #include <__type_traits/integral_constant.h> #include <__type_traits/is_nothrow_assignable.h> #include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/is_replaceable.h> #include <__type_traits/is_swappable.h> #include <__type_traits/is_trivially_destructible.h> #include <__type_traits/is_trivially_relocatable.h> @@ -72,6 +73,10 @@ public: __libcpp_is_trivially_relocatable::value && __libcpp_is_trivially_relocatable::value, __split_buffer, void>; + using __replaceable _LIBCPP_NODEBUG = + __conditional_t<__is_replaceable_v && __container_allocator_is_replaceable<__alloc_traits>::value, + __split_buffer, + void>; pointer __first_; pointer __begin_; diff --git a/libcxx/include/__type_traits/is_replaceable.h b/libcxx/include/__type_traits/is_replaceable.h new file mode 100644 index 0000000000000..e1d17c099cd3a --- /dev/null +++ b/libcxx/include/__type_traits/is_replaceable.h @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H +#define _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H + +#include <__config> +#include <__type_traits/enable_if.h> +#include <__type_traits/integral_constant.h> +#include <__type_traits/is_same.h> +#include <__type_traits/is_trivially_copyable.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +// A type is replaceable if, with `x` and `y` being different objects, `x = std::move(y)` is equivalent to: +// +// std::destroy_at(&x) +// std::construct_at(&x, std::move(y)) +// +// This allows turning a move-assignment into a sequence of destroy + move-construct, which +// is often more efficient. This is especially relevant when the move-construct is in fact +// part of a trivial relocation from somewhere else, in which case there is a huge win. +// +// Note that this requires language support in order to be really effective, but we +// currently emulate the base template with something very conservative. +template +struct __is_replaceable : is_trivially_copyable<_Tp> {}; + +template +struct __is_replaceable<_Tp, __enable_if_t::value> > : true_type {}; + +template +inline const bool __is_replaceable_v = __is_replaceable<_Tp>::value; + +// Determines whether an allocator member of a container is replaceable. +// +// First, we require the allocator type to be considered replaceable. If not, then something fishy might be +// happening. Assuming the allocator type is replaceable, we conclude replaceability of the allocator as a +// member of the container if the allocator always compares equal (in which case propagation doesn't matter), +// or if the allocator always propagates on assignment, which is required in order for move construction and +// assignment to be equivalent. +template +struct __container_allocator_is_replaceable + : integral_constant && + (_AllocatorTraits::is_always_equal::value || + (_AllocatorTraits::propagate_on_container_move_assignment::value && + _AllocatorTraits::propagate_on_container_copy_assignment::value))> {}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h index 4296caac1040a..99b0eb955b767 100644 --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -32,6 +32,8 @@ #include <__type_traits/is_implicitly_default_constructible.h> #include <__type_traits/is_nothrow_assignable.h> #include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/is_replaceable.h> +#include <__type_traits/is_same.h> #include <__type_traits/is_swappable.h> #include <__type_traits/is_trivially_relocatable.h> #include <__type_traits/nat.h> @@ -100,6 +102,7 @@ struct pair __conditional_t<__libcpp_is_trivially_relocatable<_T1>::value && __libcpp_is_trivially_relocatable<_T2>::value, pair, void>; + using __replaceable _LIBCPP_NODEBUG = __conditional_t<__is_replaceable_v<_T1> && __is_replaceable_v<_T2>, pair, void>; _LIBCPP_HIDE_FROM_ABI pair(pair const&) = default; _LIBCPP_HIDE_FROM_ABI pair(pair&&) = default; diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h index af3f7b974cdcf..c6e350a71b855 100644 --- a/libcxx/include/__vector/vector.h +++ b/libcxx/include/__vector/vector.h @@ -55,6 +55,7 @@ #include <__type_traits/is_nothrow_assignable.h> #include <__type_traits/is_nothrow_constructible.h> #include <__type_traits/is_pointer.h> +#include <__type_traits/is_replaceable.h> #include <__type_traits/is_same.h> #include <__type_traits/is_trivially_relocatable.h> #include <__type_traits/type_identity.h> @@ -120,6 +121,10 @@ class vector { __libcpp_is_trivially_relocatable::value && __libcpp_is_trivially_relocatable::value, vector, void>; + using __replaceable _LIBCPP_NODEBUG = + __conditional_t<__is_replaceable_v && __container_allocator_is_replaceable<__alloc_traits>::value, + vector, + void>; static_assert(__check_valid_allocator::value, ""); static_assert(is_same::value, diff --git a/libcxx/include/array b/libcxx/include/array index ff46838e2e8e2..9643fc1dd9dca 100644 --- a/libcxx/include/array +++ b/libcxx/include/array @@ -134,6 +134,7 @@ template const T&& get(const array&&) noexce # include <__type_traits/is_const.h> # include <__type_traits/is_constructible.h> # include <__type_traits/is_nothrow_constructible.h> +# include <__type_traits/is_replaceable.h> # include <__type_traits/is_same.h> # include <__type_traits/is_swappable.h> # include <__type_traits/is_trivially_relocatable.h> @@ -175,6 +176,7 @@ template struct array { using __trivially_relocatable _LIBCPP_NODEBUG = __conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, array, void>; + using __replaceable _LIBCPP_NODEBUG = __conditional_t<__is_replaceable_v<_Tp>, array, void>; // types: using __self _LIBCPP_NODEBUG = array; diff --git a/libcxx/include/deque b/libcxx/include/deque index e9846af5e5848..d8645d06ae59e 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -230,6 +230,7 @@ template # include <__type_traits/is_convertible.h> # include <__type_traits/is_nothrow_assignable.h> # include <__type_traits/is_nothrow_constructible.h> +# include <__type_traits/is_replaceable.h> # include <__type_traits/is_same.h> # include <__type_traits/is_swappable.h> # include <__type_traits/is_trivially_relocatable.h> @@ -530,6 +531,10 @@ public: __libcpp_is_trivially_relocatable<__map>::value && __libcpp_is_trivially_relocatable::value, deque, void>; + using __replaceable _LIBCPP_NODEBUG = + __conditional_t<__is_replaceable_v<__map> && __container_allocator_is_replaceable<__alloc_traits>::value, + deque, + void>; static_assert(is_nothrow_default_constructible::value == is_nothrow_default_constructible<__pointer_allocator>::value, diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 7260c3a5d51f3..b9cb43ebd999a 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -272,6 +272,10 @@ module std_core [system] { header "__type_traits/is_referenceable.h" export std_core.type_traits.integral_constant } + module is_replaceable { + header "__type_traits/is_replaceable.h" + export std_core.type_traits.integral_constant + } module is_same { header "__type_traits/is_same.h" export std_core.type_traits.integral_constant diff --git a/libcxx/include/optional b/libcxx/include/optional index 78aa34727c147..2153efb2ab899 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -210,6 +210,7 @@ namespace std { # include <__type_traits/is_nothrow_constructible.h> # include <__type_traits/is_object.h> # include <__type_traits/is_reference.h> +# include <__type_traits/is_replaceable.h> # include <__type_traits/is_same.h> # include <__type_traits/is_scalar.h> # include <__type_traits/is_swappable.h> @@ -590,6 +591,7 @@ public: using __trivially_relocatable _LIBCPP_NODEBUG = conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>; + using __replaceable _LIBCPP_NODEBUG = conditional_t<__is_replaceable_v<_Tp>, optional, void>; private: // Disable the reference extension using this static assert. diff --git a/libcxx/include/string b/libcxx/include/string index 33f2598f85449..4f05e211919f3 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -630,6 +630,7 @@ basic_string operator""s( const char32_t *str, size_t len ); # include <__type_traits/is_convertible.h> # include <__type_traits/is_nothrow_assignable.h> # include <__type_traits/is_nothrow_constructible.h> +# include <__type_traits/is_replaceable.h> # include <__type_traits/is_same.h> # include <__type_traits/is_standard_layout.h> # include <__type_traits/is_trivially_constructible.h> @@ -755,6 +756,9 @@ public: // external memory. In such cases, the destructor is responsible for unpoisoning // the memory to avoid triggering false positives. // Therefore it's crucial to ensure the destructor is called. + // + // However, it is replaceable since implementing move-assignment as a destroy + move-construct + // will maintain the right ASAN state. using __trivially_relocatable = void; # else using __trivially_relocatable _LIBCPP_NODEBUG = __conditional_t< @@ -762,6 +766,10 @@ public: basic_string, void>; # endif + using __replaceable _LIBCPP_NODEBUG = + __conditional_t<__is_replaceable_v && __container_allocator_is_replaceable<__alloc_traits>::value, + basic_string, + void>; # if _LIBCPP_HAS_ASAN && _LIBCPP_INSTRUMENTED_WITH_ASAN _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pointer __asan_volatile_wrapper(pointer const& __ptr) const { diff --git a/libcxx/include/tuple b/libcxx/include/tuple index f6062891823d6..8dd62ae624f5e 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -250,6 +250,7 @@ template # include <__type_traits/is_nothrow_assignable.h> # include <__type_traits/is_nothrow_constructible.h> # include <__type_traits/is_reference.h> +# include <__type_traits/is_replaceable.h> # include <__type_traits/is_same.h> # include <__type_traits/is_swappable.h> # include <__type_traits/is_trivially_relocatable.h> @@ -462,8 +463,8 @@ template struct __tuple_impl; template -struct _LIBCPP_DECLSPEC_EMPTY_BASES __tuple_impl<__tuple_indices<_Indx...>, _Tp...> - : public __tuple_leaf<_Indx, _Tp>... { +struct _LIBCPP_DECLSPEC_EMPTY_BASES + __tuple_impl<__tuple_indices<_Indx...>, _Tp...> : public __tuple_leaf<_Indx, _Tp>... { _LIBCPP_HIDE_FROM_ABI constexpr __tuple_impl() noexcept( __all::value...>::value) {} @@ -555,6 +556,7 @@ class _LIBCPP_NO_SPECIALIZATIONS tuple { public: using __trivially_relocatable _LIBCPP_NODEBUG = __conditional_t<_And<__libcpp_is_trivially_relocatable<_Tp>...>::value, tuple, void>; + using __replaceable _LIBCPP_NODEBUG = __conditional_t<_And<__is_replaceable<_Tp>...>::value, tuple, void>; // [tuple.cnstr] diff --git a/libcxx/include/variant b/libcxx/include/variant index 74a464d27ead4..23d876f9a60af 100644 --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -246,6 +246,7 @@ namespace std { # include <__type_traits/is_nothrow_assignable.h> # include <__type_traits/is_nothrow_constructible.h> # include <__type_traits/is_reference.h> +# include <__type_traits/is_replaceable.h> # include <__type_traits/is_same.h> # include <__type_traits/is_swappable.h> # include <__type_traits/is_trivially_assignable.h> @@ -850,8 +851,7 @@ _LIBCPP_VARIANT_MOVE_CONSTRUCTOR( _LIBCPP_VARIANT_MOVE_CONSTRUCTOR( _Trait::_Available, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&& __that) noexcept( - __all...>::value) - : __move_constructor(__valueless_t{}) { + __all...>::value) : __move_constructor(__valueless_t{}) { this->__generic_construct(*this, std::move(__that)); } _LIBCPP_EAT_SEMICOLON); @@ -887,8 +887,9 @@ _LIBCPP_VARIANT_COPY_CONSTRUCTOR( _LIBCPP_VARIANT_COPY_CONSTRUCTOR( _Trait::_Available, - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) - : __copy_constructor(__valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON); + _LIBCPP_HIDE_FROM_ABI + _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) : __copy_constructor( + __valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON); _LIBCPP_VARIANT_COPY_CONSTRUCTOR( _Trait::_Unavailable, @@ -1173,6 +1174,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES _LIBCPP_NO_SPECIALIZATIONS variant public: using __trivially_relocatable _LIBCPP_NODEBUG = conditional_t<_And<__libcpp_is_trivially_relocatable<_Types>...>::value, variant, void>; + using __replaceable _LIBCPP_NODEBUG = conditional_t<_And<__is_replaceable<_Types>...>::value, variant, void>; template , _Dummy>::value, int> = 0> @@ -1576,8 +1578,8 @@ visit(_Visitor&& __visitor, _Vs&&... __vs) { template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto -swap(variant<_Types...>& __lhs, - variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) -> decltype(__lhs.swap(__rhs)) { +swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) + -> decltype(__lhs.swap(__rhs)) { return __lhs.swap(__rhs); } diff --git a/libcxx/test/libcxx/type_traits/is_replaceable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_replaceable.compile.pass.cpp new file mode 100644 index 0000000000000..7735538cccae4 --- /dev/null +++ b/libcxx/test/libcxx/type_traits/is_replaceable.compile.pass.cpp @@ -0,0 +1,313 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// XFAIL: FROZEN-CXX03-HEADERS-FIXME + +#include <__type_traits/is_replaceable.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "test_allocator.h" +#include "test_macros.h" + +#ifndef TEST_HAS_NO_LOCALIZATION +# include +#endif + +template +struct NonPropagatingStatefulMoveAssignAlloc : std::allocator { + using propagate_on_container_move_assignment = std::false_type; + using is_always_equal = std::false_type; + template + struct rebind { + using other = NonPropagatingStatefulMoveAssignAlloc; + }; +}; + +template +struct NonPropagatingStatefulCopyAssignAlloc : std::allocator { + using propagate_on_container_copy_assignment = std::false_type; + using is_always_equal = std::false_type; + template + struct rebind { + using other = NonPropagatingStatefulCopyAssignAlloc; + }; +}; + +template +struct NonPropagatingStatelessMoveAssignAlloc : std::allocator { + using propagate_on_container_move_assignment = std::false_type; + using is_always_equal = std::true_type; + template + struct rebind { + using other = NonPropagatingStatelessMoveAssignAlloc; + }; +}; + +template +struct NonPropagatingStatelessCopyAssignAlloc : std::allocator { + using propagate_on_container_copy_assignment = std::false_type; + using is_always_equal = std::true_type; + template + struct rebind { + using other = NonPropagatingStatelessCopyAssignAlloc; + }; +}; + +template +struct NonReplaceableStatelessAlloc : std::allocator { + // Ensure that we don't consider an allocator that is a member of a container to be + // replaceable if it's not replaceable, even if it always compares equal and always propagates. + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_copy_assignment = std::true_type; + using is_always_equal = std::true_type; + NonReplaceableStatelessAlloc() = default; + NonReplaceableStatelessAlloc(NonReplaceableStatelessAlloc const&) {} + NonReplaceableStatelessAlloc(NonReplaceableStatelessAlloc&&) = default; + template + struct rebind { + using other = NonReplaceableStatelessAlloc; + }; +}; +static_assert(!std::__is_replaceable >::value, ""); + +static_assert(!std::__is_replaceable >::value, ""); // we use that property below + +struct Empty {}; +static_assert(std::__is_replaceable::value, ""); +static_assert(std::__is_replaceable::value, ""); +static_assert(std::__is_replaceable::value, ""); +static_assert(std::__is_replaceable::value, ""); + +struct TriviallyCopyable { + char c; + int i; + Empty s; +}; +static_assert(std::__is_replaceable::value, ""); + +struct NotTriviallyCopyable { + NotTriviallyCopyable(const NotTriviallyCopyable&); + ~NotTriviallyCopyable(); +}; +static_assert(!std::__is_replaceable::value, ""); + +struct MoveOnlyTriviallyCopyable { + MoveOnlyTriviallyCopyable(const MoveOnlyTriviallyCopyable&) = delete; + MoveOnlyTriviallyCopyable& operator=(const MoveOnlyTriviallyCopyable&) = delete; + MoveOnlyTriviallyCopyable(MoveOnlyTriviallyCopyable&&) = default; + MoveOnlyTriviallyCopyable& operator=(MoveOnlyTriviallyCopyable&&) = default; +}; +static_assert(std::__is_replaceable::value, ""); + +struct CustomCopyAssignment { + CustomCopyAssignment(const CustomCopyAssignment&) = default; + CustomCopyAssignment(CustomCopyAssignment&&) = default; + CustomCopyAssignment& operator=(const CustomCopyAssignment&); + CustomCopyAssignment& operator=(CustomCopyAssignment&&) = default; +}; +static_assert(!std::__is_replaceable::value, ""); + +struct CustomMoveAssignment { + CustomMoveAssignment(const CustomMoveAssignment&) = default; + CustomMoveAssignment(CustomMoveAssignment&&) = default; + CustomMoveAssignment& operator=(const CustomMoveAssignment&) = default; + CustomMoveAssignment& operator=(CustomMoveAssignment&&); +}; +static_assert(!std::__is_replaceable::value, ""); + +// library-internal types +// ---------------------- + +// __split_buffer +static_assert(std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable > >::value, + ""); +static_assert(!std::__is_replaceable > >::value, + ""); +static_assert(std::__is_replaceable > >::value, + ""); +static_assert(std::__is_replaceable > >::value, + ""); + +// standard library types +// ---------------------- + +// array +static_assert(std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable, 0> >::value, ""); + +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable, 1> >::value, ""); + +// basic_string +struct MyChar { + char c; +}; +template +struct NotReplaceableCharTraits : constexpr_char_traits { + NotReplaceableCharTraits(const NotReplaceableCharTraits&); + NotReplaceableCharTraits& operator=(const NotReplaceableCharTraits&); + ~NotReplaceableCharTraits(); +}; + +static_assert(std::__is_replaceable, std::allocator > >::value, + ""); +static_assert( + std::__is_replaceable, std::allocator > >::value, ""); +static_assert( + std::__is_replaceable, std::allocator > >::value, + ""); +static_assert(!std::__is_replaceable, test_allocator > >::value, + ""); +static_assert(!std::__is_replaceable< + std::basic_string, NonReplaceableStatelessAlloc > >::value, + ""); +static_assert(std::__is_replaceable< + std::basic_string, std::allocator > >::value, + ""); +static_assert( + !std::__is_replaceable< + std::basic_string, NonPropagatingStatefulCopyAssignAlloc > >::value, + ""); +static_assert( + !std::__is_replaceable< + std::basic_string, NonPropagatingStatefulMoveAssignAlloc > >::value, + ""); +static_assert( + std::__is_replaceable< + std::basic_string, NonPropagatingStatelessCopyAssignAlloc > >::value, + ""); +static_assert( + std::__is_replaceable< + std::basic_string, NonPropagatingStatelessMoveAssignAlloc > >::value, + ""); + +// deque +static_assert(std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(std::__is_replaceable > >::value, ""); +static_assert(std::__is_replaceable > >::value, ""); + +// exception_ptr +#ifndef _LIBCPP_ABI_MICROSOFT +static_assert(std::__is_replaceable::value, ""); +#endif + +// expected +#if TEST_STD_VER >= 23 +static_assert(std::__is_replaceable >::value); +static_assert(!std::__is_replaceable>::value); +static_assert(!std::__is_replaceable>::value); +static_assert(!std::__is_replaceable>::value); +#endif + +// locale +#ifndef TEST_HAS_NO_LOCALIZATION +static_assert(std::__is_replaceable::value, ""); +#endif + +// optional +#if TEST_STD_VER >= 17 +static_assert(std::__is_replaceable>::value, ""); +static_assert(!std::__is_replaceable>::value, ""); +#endif + +// pair +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); + +// shared_ptr +static_assert(std::__is_replaceable >::value, ""); + +// tuple +#if TEST_STD_VER >= 11 +static_assert(std::__is_replaceable >::value, ""); + +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); + +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +#endif // TEST_STD_VER >= 11 + +// unique_ptr +struct NonReplaceableDeleter { + NonReplaceableDeleter(const NonReplaceableDeleter&); + NonReplaceableDeleter& operator=(const NonReplaceableDeleter&); + ~NonReplaceableDeleter(); + + template + void operator()(T*); +}; + +struct NonReplaceablePointer { + struct pointer { + pointer(const pointer&); + pointer& operator=(const pointer&); + ~pointer(); + }; + + template + void operator()(T*); +}; + +static_assert(std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); + +// variant +#if TEST_STD_VER >= 17 +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); + +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable >::value, ""); +#endif // TEST_STD_VER >= 17 + +// vector +static_assert(std::__is_replaceable >::value, ""); +static_assert(std::__is_replaceable >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(!std::__is_replaceable > >::value, ""); +static_assert(std::__is_replaceable > >::value, ""); +static_assert(std::__is_replaceable > >::value, ""); + +// weak_ptr +static_assert(std::__is_replaceable >::value, ""); + +// TODO: Mark all the replaceable STL types as such