Skip to content

Commit

Permalink
[libc++] Forbid default_delete<Derived> to convert to default_delete<…
Browse files Browse the repository at this point in the history
…NonVirtualBase>

Per an idea by Lénárd Szolnoki.
In 2023, Peter Sommerlad was considering proposing this to WG21.
Also, implement `is_similar_v`.
  • Loading branch information
Quuxplusone committed Jun 16, 2024
1 parent 6f67efe commit 064121a
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 1 deletion.
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ set(files
__type_traits/is_scoped_enum.h
__type_traits/is_signed.h
__type_traits/is_signed_integer.h
__type_traits/is_similar.h
__type_traits/is_specialization.h
__type_traits/is_standard_layout.h
__type_traits/is_swappable.h
Expand Down
9 changes: 9 additions & 0 deletions libcxx/include/__concepts/same_as.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <__config>
#include <__type_traits/is_same.h>
#include <__type_traits/is_similar.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
Expand All @@ -28,6 +29,14 @@ concept __same_as_impl = _IsSame<_Tp, _Up>::value;
template <class _Tp, class _Up>
concept same_as = __same_as_impl<_Tp, _Up> && __same_as_impl<_Up, _Tp>;

// [concept.similar]

template <class _Tp, class _Up>
concept __similar_to_impl = is_similar<_Tp, _Up>::value;

template <class _Tp, class _Up>
concept similar_to = (__similar_to_impl<_Tp, _Up> && __similar_to_impl<_Up, _Tp>) || same_as<_Tp, _Up>;

#endif // _LIBCPP_STD_VER >= 20

_LIBCPP_END_NAMESPACE_STD
Expand Down
10 changes: 9 additions & 1 deletion libcxx/include/__memory/unique_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <__type_traits/conditional.h>
#include <__type_traits/dependent_type.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/has_virtual_destructor.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_assignable.h>
#include <__type_traits/is_constructible.h>
Expand All @@ -32,6 +33,7 @@
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_similar.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/is_void.h>
Expand All @@ -50,6 +52,12 @@ _LIBCPP_PUSH_MACROS

_LIBCPP_BEGIN_NAMESPACE_STD

template <class _Tp, class _Up, bool _Enable>
struct _HasVirtualDestructor : has_virtual_destructor<_Tp> { };

template <class _Tp, class _Up>
struct _HasVirtualDestructor<_Tp, _Up, true> : true_type { };

template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS default_delete {
static_assert(!is_function<_Tp>::value, "default_delete cannot be instantiated for function types");
Expand All @@ -58,7 +66,7 @@ struct _LIBCPP_TEMPLATE_VIS default_delete {
#else
_LIBCPP_HIDE_FROM_ABI default_delete() {}
#endif
template <class _Up, __enable_if_t<is_convertible<_Up*, _Tp*>::value, int> = 0>
template <class _Up, __enable_if_t<is_convertible<_Up*, _Tp*>::value && _HasVirtualDestructor<_Tp, _Up, is_similar<_Tp, _Up>::value>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 default_delete(const default_delete<_Up>&) _NOEXCEPT {}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void operator()(_Tp* __ptr) const _NOEXCEPT {
Expand Down
60 changes: 60 additions & 0 deletions libcxx/include/__type_traits/is_similar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___TYPE_TRAITS_IS_SIMILAR_H
#define _LIBCPP___TYPE_TRAITS_IS_SIMILAR_H

#include <__config>
#include <__type_traits/conditional.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_const.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_volatile.h>
#include <__type_traits/remove_cv.h>
#include <cstddef>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

template <class _Tp, class _Up>
struct is_similar : conditional<
is_const<_Tp>::value || is_volatile<_Tp>::value ||
is_const<_Up>::value || is_volatile<_Up>::value,
is_similar<typename remove_cv<_Tp>::type, typename remove_cv<_Up>::type>,
is_same<_Tp, _Up>
>::type {};

template <class _Tp, class _Up>
struct is_similar<_Tp*, _Up*>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, class _Cp>
struct is_similar<_Tp _Cp::*, _Up _Cp::*>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up>
struct is_similar<_Tp[], _Up[]>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, size_t _Np>
struct is_similar<_Tp[], _Up[_Np]>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, size_t _Np>
struct is_similar<_Tp[_Np], _Up[]>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, size_t _Np>
struct is_similar<_Tp[_Np], _Up[_Np]>: is_similar<_Tp, _Up> {};

#if _LIBCPP_STD_VER > 14
template <class _Tp, class _Up>
inline constexpr bool is_similar_v = is_similar<_Tp, _Up>::value;
#endif

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___TYPE_TRAITS_IS_SIMILAR_H
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,7 @@ module std_private_type_traits_is_scalar [system
module std_private_type_traits_is_scoped_enum [system] { header "__type_traits/is_scoped_enum.h" }
module std_private_type_traits_is_signed [system] { header "__type_traits/is_signed.h" }
module std_private_type_traits_is_signed_integer [system] { header "__type_traits/is_signed_integer.h" }
module std_private_type_traits_is_similar [system] { header "__type_traits/is_similar.h" }
module std_private_type_traits_is_specialization [system] { header "__type_traits/is_specialization.h" }
module std_private_type_traits_is_standard_layout [system] { header "__type_traits/is_standard_layout.h" }
module std_private_type_traits_is_swappable [system] {
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ namespace std
#include <__type_traits/is_scalar.h>
#include <__type_traits/is_scoped_enum.h>
#include <__type_traits/is_signed.h>
#include <__type_traits/is_similar.h>
#include <__type_traits/is_specialization.h>
#include <__type_traits/is_standard_layout.h>
#include <__type_traits/is_swappable.h>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts

// template<class T, class U>
// concept similar_to;

#include <concepts>
#include <type_traits>

struct S;

void CheckSimilarTo() {
static_assert(std::similar_to<int, int>);
static_assert(std::similar_to<int*, int*>);
static_assert(std::similar_to<int[], int[]>);
static_assert(std::similar_to<int[9], int[]>);
static_assert(std::similar_to<int[], int[9]>);
static_assert(std::similar_to<int[9], int[9]>);

static_assert(std::similar_to<const int, int>);
static_assert(std::similar_to<int const*, int*>);
static_assert(std::similar_to<int *const, int*>);
static_assert(std::similar_to<int const[], int[]>);
static_assert(std::similar_to<int const[9], int[]>);
static_assert(std::similar_to<int const[], int[9]>);
static_assert(std::similar_to<int const[9], int[9]>);

static_assert(std::similar_to<int, const int>);
static_assert(std::similar_to<int*, int const*>);
static_assert(std::similar_to<int*, int *const>);
static_assert(std::similar_to<int[], int const[]>);
static_assert(std::similar_to<int[9], int const[]>);
static_assert(std::similar_to<int[], int const[9]>);
static_assert(std::similar_to<int[9], int const[9]>);

static_assert(!std::similar_to<int&, int>);
static_assert(!std::similar_to<int, int&>);
static_assert(!std::similar_to<int&&, int>);
static_assert(!std::similar_to<int, int&&>);
static_assert(!std::similar_to<int&&, int&>);
static_assert(!std::similar_to<int&, int&&>);

static_assert(std::similar_to<int S::*, int S::*>);
static_assert(std::similar_to<int& (S::*)(), int& (S::*)()>);
static_assert(std::similar_to<int& (S::*)(int*), int& (S::*)(int*)>);

static_assert(std::similar_to<int S::*, int S::*const>);
static_assert(std::similar_to<int& (S::*)(), int& (S::*const)()>);
static_assert(std::similar_to<int& (S::*)(int*), int& (S::*const)(int*)>);

static_assert(!std::similar_to<int S::*, char S::*>);
static_assert(!std::similar_to<int& (S::*)(), const int& (S::*)()>);
static_assert(!std::similar_to<int& (S::*)(), int (S::*)()>);
static_assert(!std::similar_to<int& (S::*)(), int (S::*)()>);
static_assert(!std::similar_to<int (S::*)(char), int (S::*)(int)>);
static_assert(!std::similar_to<int& (S::*)(int*), int& (S::*)(int const*)>);
}

void CheckFunctionTypes() {
static_assert(std::similar_to<int(), int()>);
static_assert(std::similar_to<int(int), int(int)>);
static_assert(std::similar_to<int(*)(int), int(*const)(int)>);

static_assert(!std::similar_to<int(), int(int)>);
static_assert(!std::similar_to<int(), void()>);
static_assert(!std::similar_to<int(), int(*)()>);
static_assert(!std::similar_to<int(int*), int(int const*)>);
static_assert(!std::similar_to<int*(), int const*()>);
}

void CheckArraysofUnknownBound() {
static_assert(std::similar_to<int[], int[9]>);
static_assert(std::similar_to<int[9], int[]>);
static_assert(std::similar_to<int(*)[], int(*)[9]>);
static_assert(std::similar_to<int(*)[9], int(*)[]>);
static_assert(std::similar_to<int(*)[], int(*const)[9]>);
static_assert(std::similar_to<int(*)[9], int(*const)[]>);
static_assert(std::similar_to<int(*const*)[], int(**)[9]>);
static_assert(std::similar_to<int(*const*)[9], int(**)[]>);
static_assert(std::similar_to<int *(*const*)[], int *const(**)[9]>);
static_assert(std::similar_to<int *(*const*)[9], int *const(**)[]>);

static_assert(!std::similar_to<int[9], int[10]>);
static_assert(!std::similar_to<int(*)[9], int(*)[10]>);
static_assert(!std::similar_to<int *(*const*)[9], int *const(**)[10]>);
}


template <class T, class U> requires std::same_as<T, U>
void SubsumptionTest1();

template <class T, class U> requires std::similar_to<U, T>
int SubsumptionTest1();

static_assert(std::same_as<void, decltype(SubsumptionTest1<int, int>())>);
static_assert(std::same_as<int, decltype(SubsumptionTest1<int, const int>())>);


template <class T, class U> requires std::similar_to<T, U>
void SubsumptionTest2();

template <class T, class U> requires std::similar_to<U, T> && (sizeof(T) > 0)
int SubsumptionTest2();

static_assert(std::same_as<int, decltype(SubsumptionTest2<int, int>())>);
static_assert(std::same_as<int, decltype(SubsumptionTest2<int, const int>())>);


int main(int, char**) {
CheckSimilarTo();
return 0;
}
67 changes: 67 additions & 0 deletions libcxx/test/std/utilities/meta/meta.rel/is_similar.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// type_traits

// is_same

#include <type_traits>

struct S;

void CheckIsSimilar() {
static_assert(std::is_similar<int, int>::value, "");
static_assert(std::is_similar<int*, int*>::value, "");
static_assert(std::is_similar<int[], int[]>::value, "");
static_assert(std::is_similar<int[9], int[]>::value, "");
static_assert(std::is_similar<int[], int[9]>::value, "");
static_assert(std::is_similar<int[9], int[9]>::value, "");

static_assert(std::is_similar<const int, int>::value, "");
static_assert(std::is_similar<int const*, int*>::value, "");
static_assert(std::is_similar<int *const, int*>::value, "");
static_assert(std::is_similar<int const[], int[]>::value, "");
static_assert(std::is_similar<int const[9], int[]>::value, "");
static_assert(std::is_similar<int const[], int[9]>::value, "");
static_assert(std::is_similar<int const[9], int[9]>::value, "");

static_assert(std::is_similar<int, const int>::value, "");
static_assert(std::is_similar<int*, int const*>::value, "");
static_assert(std::is_similar<int*, int *const>::value, "");
static_assert(std::is_similar<int[], int const[]>::value, "");
static_assert(std::is_similar<int[9], int const[]>::value, "");
static_assert(std::is_similar<int[], int const[9]>::value, "");
static_assert(std::is_similar<int[9], int const[9]>::value, "");

static_assert(!std::is_similar<int&, int>::value, "");
static_assert(!std::is_similar<int, int&>::value, "");
static_assert(!std::is_similar<int&&, int>::value, "");
static_assert(!std::is_similar<int, int&&>::value, "");
static_assert(!std::is_similar<int&&, int&>::value, "");
static_assert(!std::is_similar<int&, int&&>::value, "");

static_assert(std::is_similar<int S::*, int S::*>::value, "");
static_assert(std::is_similar<int& (S::*)(), int& (S::*)()>::value, "");
static_assert(std::is_similar<int& (S::*)(int*), int& (S::*)(int*)>::value, "");

static_assert(std::is_similar<int S::*, int S::*const>::value, "");
static_assert(std::is_similar<int& (S::*)(), int& (S::*const)()>::value, "");
static_assert(std::is_similar<int& (S::*)(int*), int& (S::*const)(int*)>::value, "");

static_assert(!std::is_similar<int S::*, char S::*>::value, "");
static_assert(!std::is_similar<int& (S::*)(), const int& (S::*)()>::value, "");
static_assert(!std::is_similar<int& (S::*)(), int (S::*)()>::value, "");
static_assert(!std::is_similar<int& (S::*)(), int (S::*)()>::value, "");
static_assert(!std::is_similar<int (S::*)(char), int (S::*)(int)>::value, "");
static_assert(!std::is_similar<int& (S::*)(int*), int& (S::*)(int const*)>::value, "");
}

int main(int, char**) {
CheckIsSimilar();
return 0;
}

0 comments on commit 064121a

Please sign in to comment.