[libc++] Add the __is_replaceable type trait (#132408)
That type trait represents whether move-assigning an object is equivalent to destroying it and then move-constructing a new one from the same argument. This will be useful in a few places where we may want to destroy + construct instead of doing an assignment, in particular when implementing some container operations in terms of relocation. This is effectively adding a library emulation of P2786R12's is_replaceable trait, similarly to what we do for trivial relocation. Eventually, we can replace this library emulation by the real compiler-backed trait. This is building towards #129328.
This commit is contained in:
parent
c82e2f5c9e
commit
45d493b680
@ -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
|
||||
|
@ -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_() {}
|
||||
|
@ -30,6 +30,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>
|
||||
@ -471,6 +472,8 @@ public:
|
||||
__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 <class _Up>
|
||||
using rebind = expected<_Up, error_type>;
|
||||
|
@ -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;
|
||||
|
@ -316,8 +316,10 @@ public:
|
||||
#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 @@ public:
|
||||
#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_;
|
||||
|
@ -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 @@ public:
|
||||
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
|
||||
unique_ptr,
|
||||
void>;
|
||||
using __replaceable _LIBCPP_NODEBUG =
|
||||
__conditional_t<__is_replaceable_v<pointer> && __is_replaceable_v<deleter_type>, unique_ptr, void>;
|
||||
|
||||
private:
|
||||
_LIBCPP_COMPRESSED_PAIR(pointer, __ptr_, deleter_type, __deleter_);
|
||||
@ -410,6 +413,8 @@ public:
|
||||
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
|
||||
unique_ptr,
|
||||
void>;
|
||||
using __replaceable _LIBCPP_NODEBUG =
|
||||
__conditional_t<__is_replaceable_v<pointer> && __is_replaceable_v<deleter_type>, unique_ptr, void>;
|
||||
|
||||
private:
|
||||
template <class _Up, class _OtherDeleter>
|
||||
|
@ -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<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
|
||||
__split_buffer,
|
||||
void>;
|
||||
using __replaceable _LIBCPP_NODEBUG =
|
||||
__conditional_t<__is_replaceable_v<pointer> && __container_allocator_is_replaceable<__alloc_traits>::value,
|
||||
__split_buffer,
|
||||
void>;
|
||||
|
||||
pointer __first_;
|
||||
pointer __begin_;
|
||||
|
61
libcxx/include/__type_traits/is_replaceable.h
Normal file
61
libcxx/include/__type_traits/is_replaceable.h
Normal file
@ -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 <class _Tp, class = void>
|
||||
struct __is_replaceable : is_trivially_copyable<_Tp> {};
|
||||
|
||||
template <class _Tp>
|
||||
struct __is_replaceable<_Tp, __enable_if_t<is_same<_Tp, typename _Tp::__replaceable>::value> > : true_type {};
|
||||
|
||||
template <class _Tp>
|
||||
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 <class _AllocatorTraits>
|
||||
struct __container_allocator_is_replaceable
|
||||
: integral_constant<bool,
|
||||
__is_replaceable_v<typename _AllocatorTraits::allocator_type> &&
|
||||
(_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
|
@ -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;
|
||||
|
@ -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 @@ public:
|
||||
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
|
||||
vector,
|
||||
void>;
|
||||
using __replaceable _LIBCPP_NODEBUG =
|
||||
__conditional_t<__is_replaceable_v<pointer> && __container_allocator_is_replaceable<__alloc_traits>::value,
|
||||
vector,
|
||||
void>;
|
||||
|
||||
static_assert(__check_valid_allocator<allocator_type>::value, "");
|
||||
static_assert(is_same<typename allocator_type::value_type, value_type>::value,
|
||||
|
@ -134,6 +134,7 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) 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 <class _Tp, size_t _Size>
|
||||
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;
|
||||
|
@ -230,6 +230,7 @@ template <class T, class Allocator, class Predicate>
|
||||
# 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<allocator_type>::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<allocator_type>::value ==
|
||||
is_nothrow_default_constructible<__pointer_allocator>::value,
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -630,6 +630,7 @@ basic_string<char32_t> 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<pointer> && __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 {
|
||||
|
@ -250,6 +250,7 @@ template <class... Types>
|
||||
# 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 <class _Indx, class... _Tp>
|
||||
struct __tuple_impl;
|
||||
|
||||
template <size_t... _Indx, class... _Tp>
|
||||
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<is_nothrow_default_constructible<_Tp>::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]
|
||||
|
||||
|
@ -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<is_nothrow_move_constructible_v<_Types>...>::value)
|
||||
: __move_constructor(__valueless_t{}) {
|
||||
__all<is_nothrow_move_constructible_v<_Types>...>::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 <bool _Dummy = true,
|
||||
enable_if_t<__dependent_type<is_default_constructible<__first_type>, _Dummy>::value, int> = 0>
|
||||
@ -1576,8 +1578,8 @@ visit(_Visitor&& __visitor, _Vs&&... __vs) {
|
||||
|
||||
template <class... _Types>
|
||||
_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);
|
||||
}
|
||||
|
||||
|
313
libcxx/test/libcxx/type_traits/is_replaceable.compile.pass.cpp
Normal file
313
libcxx/test/libcxx/type_traits/is_replaceable.compile.pass.cpp
Normal file
@ -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 <array>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "constexpr_char_traits.h"
|
||||
#include "test_allocator.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
#ifndef TEST_HAS_NO_LOCALIZATION
|
||||
# include <locale>
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct NonPropagatingStatefulMoveAssignAlloc : std::allocator<T> {
|
||||
using propagate_on_container_move_assignment = std::false_type;
|
||||
using is_always_equal = std::false_type;
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = NonPropagatingStatefulMoveAssignAlloc<U>;
|
||||
};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct NonPropagatingStatefulCopyAssignAlloc : std::allocator<T> {
|
||||
using propagate_on_container_copy_assignment = std::false_type;
|
||||
using is_always_equal = std::false_type;
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = NonPropagatingStatefulCopyAssignAlloc<U>;
|
||||
};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct NonPropagatingStatelessMoveAssignAlloc : std::allocator<T> {
|
||||
using propagate_on_container_move_assignment = std::false_type;
|
||||
using is_always_equal = std::true_type;
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = NonPropagatingStatelessMoveAssignAlloc<U>;
|
||||
};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct NonPropagatingStatelessCopyAssignAlloc : std::allocator<T> {
|
||||
using propagate_on_container_copy_assignment = std::false_type;
|
||||
using is_always_equal = std::true_type;
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = NonPropagatingStatelessCopyAssignAlloc<U>;
|
||||
};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct NonReplaceableStatelessAlloc : std::allocator<T> {
|
||||
// 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 <class U>
|
||||
struct rebind {
|
||||
using other = NonReplaceableStatelessAlloc<U>;
|
||||
};
|
||||
};
|
||||
static_assert(!std::__is_replaceable<NonReplaceableStatelessAlloc<int> >::value, "");
|
||||
|
||||
static_assert(!std::__is_replaceable<test_allocator<char> >::value, ""); // we use that property below
|
||||
|
||||
struct Empty {};
|
||||
static_assert(std::__is_replaceable<char>::value, "");
|
||||
static_assert(std::__is_replaceable<int>::value, "");
|
||||
static_assert(std::__is_replaceable<double>::value, "");
|
||||
static_assert(std::__is_replaceable<Empty>::value, "");
|
||||
|
||||
struct TriviallyCopyable {
|
||||
char c;
|
||||
int i;
|
||||
Empty s;
|
||||
};
|
||||
static_assert(std::__is_replaceable<TriviallyCopyable>::value, "");
|
||||
|
||||
struct NotTriviallyCopyable {
|
||||
NotTriviallyCopyable(const NotTriviallyCopyable&);
|
||||
~NotTriviallyCopyable();
|
||||
};
|
||||
static_assert(!std::__is_replaceable<NotTriviallyCopyable>::value, "");
|
||||
|
||||
struct MoveOnlyTriviallyCopyable {
|
||||
MoveOnlyTriviallyCopyable(const MoveOnlyTriviallyCopyable&) = delete;
|
||||
MoveOnlyTriviallyCopyable& operator=(const MoveOnlyTriviallyCopyable&) = delete;
|
||||
MoveOnlyTriviallyCopyable(MoveOnlyTriviallyCopyable&&) = default;
|
||||
MoveOnlyTriviallyCopyable& operator=(MoveOnlyTriviallyCopyable&&) = default;
|
||||
};
|
||||
static_assert(std::__is_replaceable<MoveOnlyTriviallyCopyable>::value, "");
|
||||
|
||||
struct CustomCopyAssignment {
|
||||
CustomCopyAssignment(const CustomCopyAssignment&) = default;
|
||||
CustomCopyAssignment(CustomCopyAssignment&&) = default;
|
||||
CustomCopyAssignment& operator=(const CustomCopyAssignment&);
|
||||
CustomCopyAssignment& operator=(CustomCopyAssignment&&) = default;
|
||||
};
|
||||
static_assert(!std::__is_replaceable<CustomCopyAssignment>::value, "");
|
||||
|
||||
struct CustomMoveAssignment {
|
||||
CustomMoveAssignment(const CustomMoveAssignment&) = default;
|
||||
CustomMoveAssignment(CustomMoveAssignment&&) = default;
|
||||
CustomMoveAssignment& operator=(const CustomMoveAssignment&) = default;
|
||||
CustomMoveAssignment& operator=(CustomMoveAssignment&&);
|
||||
};
|
||||
static_assert(!std::__is_replaceable<CustomMoveAssignment>::value, "");
|
||||
|
||||
// library-internal types
|
||||
// ----------------------
|
||||
|
||||
// __split_buffer
|
||||
static_assert(std::__is_replaceable<std::__split_buffer<int> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::__split_buffer<NotTriviallyCopyable> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatefulCopyAssignAlloc<int> > >::value,
|
||||
"");
|
||||
static_assert(!std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatefulMoveAssignAlloc<int> > >::value,
|
||||
"");
|
||||
static_assert(std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatelessCopyAssignAlloc<int> > >::value,
|
||||
"");
|
||||
static_assert(std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatelessMoveAssignAlloc<int> > >::value,
|
||||
"");
|
||||
|
||||
// standard library types
|
||||
// ----------------------
|
||||
|
||||
// array
|
||||
static_assert(std::__is_replaceable<std::array<int, 0> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::array<NotTriviallyCopyable, 0> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::array<std::unique_ptr<int>, 0> >::value, "");
|
||||
|
||||
static_assert(std::__is_replaceable<std::array<int, 1> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::array<NotTriviallyCopyable, 1> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::array<std::unique_ptr<int>, 1> >::value, "");
|
||||
|
||||
// basic_string
|
||||
struct MyChar {
|
||||
char c;
|
||||
};
|
||||
template <class T>
|
||||
struct NotReplaceableCharTraits : constexpr_char_traits<T> {
|
||||
NotReplaceableCharTraits(const NotReplaceableCharTraits&);
|
||||
NotReplaceableCharTraits& operator=(const NotReplaceableCharTraits&);
|
||||
~NotReplaceableCharTraits();
|
||||
};
|
||||
|
||||
static_assert(std::__is_replaceable<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::value,
|
||||
"");
|
||||
static_assert(
|
||||
std::__is_replaceable<std::basic_string<char, NotReplaceableCharTraits<char>, std::allocator<char> > >::value, "");
|
||||
static_assert(
|
||||
std::__is_replaceable<std::basic_string<MyChar, constexpr_char_traits<MyChar>, std::allocator<MyChar> > >::value,
|
||||
"");
|
||||
static_assert(!std::__is_replaceable<std::basic_string<char, std::char_traits<char>, test_allocator<char> > >::value,
|
||||
"");
|
||||
static_assert(!std::__is_replaceable<
|
||||
std::basic_string<char, std::char_traits<char>, NonReplaceableStatelessAlloc<char> > >::value,
|
||||
"");
|
||||
static_assert(std::__is_replaceable<
|
||||
std::basic_string<MyChar, NotReplaceableCharTraits<MyChar>, std::allocator<MyChar> > >::value,
|
||||
"");
|
||||
static_assert(
|
||||
!std::__is_replaceable<
|
||||
std::basic_string<char, std::char_traits<char>, NonPropagatingStatefulCopyAssignAlloc<char> > >::value,
|
||||
"");
|
||||
static_assert(
|
||||
!std::__is_replaceable<
|
||||
std::basic_string<char, std::char_traits<char>, NonPropagatingStatefulMoveAssignAlloc<char> > >::value,
|
||||
"");
|
||||
static_assert(
|
||||
std::__is_replaceable<
|
||||
std::basic_string<char, std::char_traits<char>, NonPropagatingStatelessCopyAssignAlloc<char> > >::value,
|
||||
"");
|
||||
static_assert(
|
||||
std::__is_replaceable<
|
||||
std::basic_string<char, std::char_traits<char>, NonPropagatingStatelessMoveAssignAlloc<char> > >::value,
|
||||
"");
|
||||
|
||||
// deque
|
||||
static_assert(std::__is_replaceable<std::deque<int> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::deque<NotTriviallyCopyable> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::deque<int, test_allocator<int> > >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::deque<int, NonReplaceableStatelessAlloc<int> > >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::deque<int, NonPropagatingStatefulCopyAssignAlloc<int> > >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::deque<int, NonPropagatingStatefulMoveAssignAlloc<int> > >::value, "");
|
||||
static_assert(std::__is_replaceable<std::deque<int, NonPropagatingStatelessCopyAssignAlloc<int> > >::value, "");
|
||||
static_assert(std::__is_replaceable<std::deque<int, NonPropagatingStatelessMoveAssignAlloc<int> > >::value, "");
|
||||
|
||||
// exception_ptr
|
||||
#ifndef _LIBCPP_ABI_MICROSOFT
|
||||
static_assert(std::__is_replaceable<std::exception_ptr>::value, "");
|
||||
#endif
|
||||
|
||||
// expected
|
||||
#if TEST_STD_VER >= 23
|
||||
static_assert(std::__is_replaceable<std::expected<int, int> >::value);
|
||||
static_assert(!std::__is_replaceable<std::expected<CustomCopyAssignment, int>>::value);
|
||||
static_assert(!std::__is_replaceable<std::expected<int, CustomCopyAssignment>>::value);
|
||||
static_assert(!std::__is_replaceable<std::expected<CustomCopyAssignment, CustomCopyAssignment>>::value);
|
||||
#endif
|
||||
|
||||
// locale
|
||||
#ifndef TEST_HAS_NO_LOCALIZATION
|
||||
static_assert(std::__is_replaceable<std::locale>::value, "");
|
||||
#endif
|
||||
|
||||
// optional
|
||||
#if TEST_STD_VER >= 17
|
||||
static_assert(std::__is_replaceable<std::optional<int>>::value, "");
|
||||
static_assert(!std::__is_replaceable<std::optional<CustomCopyAssignment>>::value, "");
|
||||
#endif
|
||||
|
||||
// pair
|
||||
static_assert(std::__is_replaceable<std::pair<int, int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::pair<CustomCopyAssignment, int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::pair<int, CustomCopyAssignment> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::pair<CustomCopyAssignment, CustomCopyAssignment> >::value, "");
|
||||
|
||||
// shared_ptr
|
||||
static_assert(std::__is_replaceable<std::shared_ptr<int> >::value, "");
|
||||
|
||||
// tuple
|
||||
#if TEST_STD_VER >= 11
|
||||
static_assert(std::__is_replaceable<std::tuple<> >::value, "");
|
||||
|
||||
static_assert(std::__is_replaceable<std::tuple<int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::tuple<CustomCopyAssignment> >::value, "");
|
||||
|
||||
static_assert(std::__is_replaceable<std::tuple<int, int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::tuple<CustomCopyAssignment, int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::tuple<int, CustomCopyAssignment> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::tuple<CustomCopyAssignment, CustomCopyAssignment> >::value, "");
|
||||
#endif // TEST_STD_VER >= 11
|
||||
|
||||
// unique_ptr
|
||||
struct NonReplaceableDeleter {
|
||||
NonReplaceableDeleter(const NonReplaceableDeleter&);
|
||||
NonReplaceableDeleter& operator=(const NonReplaceableDeleter&);
|
||||
~NonReplaceableDeleter();
|
||||
|
||||
template <class T>
|
||||
void operator()(T*);
|
||||
};
|
||||
|
||||
struct NonReplaceablePointer {
|
||||
struct pointer {
|
||||
pointer(const pointer&);
|
||||
pointer& operator=(const pointer&);
|
||||
~pointer();
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void operator()(T*);
|
||||
};
|
||||
|
||||
static_assert(std::__is_replaceable<std::unique_ptr<int> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::unique_ptr<CustomCopyAssignment> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::unique_ptr<int[]> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::unique_ptr<int, NonReplaceableDeleter> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::unique_ptr<int[], NonReplaceableDeleter> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::unique_ptr<int, NonReplaceablePointer> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::unique_ptr<int[], NonReplaceablePointer> >::value, "");
|
||||
|
||||
// variant
|
||||
#if TEST_STD_VER >= 17
|
||||
static_assert(std::__is_replaceable<std::variant<int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::variant<CustomCopyAssignment> >::value, "");
|
||||
|
||||
static_assert(std::__is_replaceable<std::variant<int, int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::variant<CustomCopyAssignment, int> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::variant<int, CustomCopyAssignment> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::variant<CustomCopyAssignment, CustomCopyAssignment> >::value, "");
|
||||
#endif // TEST_STD_VER >= 17
|
||||
|
||||
// vector
|
||||
static_assert(std::__is_replaceable<std::vector<int> >::value, "");
|
||||
static_assert(std::__is_replaceable<std::vector<CustomCopyAssignment> >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::vector<int, test_allocator<int> > >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::vector<int, NonReplaceableStatelessAlloc<int> > >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::vector<int, NonPropagatingStatefulCopyAssignAlloc<int> > >::value, "");
|
||||
static_assert(!std::__is_replaceable<std::vector<int, NonPropagatingStatefulMoveAssignAlloc<int> > >::value, "");
|
||||
static_assert(std::__is_replaceable<std::vector<int, NonPropagatingStatelessCopyAssignAlloc<int> > >::value, "");
|
||||
static_assert(std::__is_replaceable<std::vector<int, NonPropagatingStatelessMoveAssignAlloc<int> > >::value, "");
|
||||
|
||||
// weak_ptr
|
||||
static_assert(std::__is_replaceable<std::weak_ptr<CustomCopyAssignment> >::value, "");
|
||||
|
||||
// TODO: Mark all the replaceable STL types as such
|
Loading…
x
Reference in New Issue
Block a user