1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00

ADT: Add SFINAE to the generic IntrusiveRefCntPtr constructors

Add an `enable_if` to the generic `IntrusiveRefCntPtr` constructors so
that std::is_convertible gives an honest answer when the underlying
pointers cannot be converted. Added `static_assert`s to the test suite
to verify.

Also combine generic constructors from `IntrusiveRefCntPtr<X>&&` and
`const IntrusiveRefCntPtr<X>&`. At first glance this appears to be an
infinite loop, but the real copy/move constructors are spelled out
separately above. Added a unit test to verify.

Differential Revision: https://reviews.llvm.org/D95498
This commit is contained in:
Duncan P. N. Exon Smith 2021-01-26 19:51:22 -08:00
parent c1671731c6
commit a126d2972b
2 changed files with 51 additions and 8 deletions

View File

@ -171,21 +171,18 @@ public:
IntrusiveRefCntPtr(const IntrusiveRefCntPtr &S) : Obj(S.Obj) { retain(); }
IntrusiveRefCntPtr(IntrusiveRefCntPtr &&S) : Obj(S.Obj) { S.Obj = nullptr; }
template <class X>
IntrusiveRefCntPtr(IntrusiveRefCntPtr<X> &&S) : Obj(S.get()) {
template <class X,
std::enable_if_t<std::is_convertible<X *, T *>::value, bool> = true>
IntrusiveRefCntPtr(IntrusiveRefCntPtr<X> S) : Obj(S.get()) {
S.Obj = nullptr;
}
template <class X>
template <class X,
std::enable_if_t<std::is_convertible<X *, T *>::value, bool> = true>
IntrusiveRefCntPtr(std::unique_ptr<X> S) : Obj(S.release()) {
retain();
}
template <class X>
IntrusiveRefCntPtr(const IntrusiveRefCntPtr<X> &S) : Obj(S.get()) {
retain();
}
~IntrusiveRefCntPtr() { release(); }
IntrusiveRefCntPtr &operator=(IntrusiveRefCntPtr S) {

View File

@ -96,4 +96,50 @@ TEST(IntrusiveRefCntPtr, UsesTraitsToRetainAndRelease) {
EXPECT_TRUE(Retained);
}
// Test that the generic constructors use SFINAE to disable invalid
// conversions.
struct X : RefCountedBase<X> {};
struct Y : X {};
struct Z : RefCountedBase<Z> {};
static_assert(!std::is_convertible<IntrusiveRefCntPtr<X> &&,
IntrusiveRefCntPtr<Y>>::value,
"X&& -> Y should be rejected with SFINAE");
static_assert(!std::is_convertible<const IntrusiveRefCntPtr<X> &,
IntrusiveRefCntPtr<Y>>::value,
"const X& -> Y should be rejected with SFINAE");
static_assert(
!std::is_convertible<std::unique_ptr<X>, IntrusiveRefCntPtr<Y>>::value,
"X -> Y should be rejected with SFINAE");
static_assert(!std::is_convertible<IntrusiveRefCntPtr<X> &&,
IntrusiveRefCntPtr<Z>>::value,
"X&& -> Z should be rejected with SFINAE");
static_assert(!std::is_convertible<const IntrusiveRefCntPtr<X> &,
IntrusiveRefCntPtr<Z>>::value,
"cosnt X& -> Z should be rejected with SFINAE");
static_assert(
!std::is_convertible<std::unique_ptr<X>, IntrusiveRefCntPtr<Z>>::value,
"X -> Z should be rejected with SFINAE");
TEST(IntrusiveRefCntPtr, InteropsWithConvertible) {
// Check converting constructors and operator=.
auto Y1 = makeIntrusiveRefCnt<Y>();
auto Y2 = makeIntrusiveRefCnt<Y>();
auto Y3 = makeIntrusiveRefCnt<Y>();
auto Y4 = makeIntrusiveRefCnt<Y>();
const void *P1 = Y1.get();
const void *P2 = Y2.get();
const void *P3 = Y3.get();
const void *P4 = Y4.get();
IntrusiveRefCntPtr<X> X1 = std::move(Y1);
IntrusiveRefCntPtr<X> X2 = Y2;
IntrusiveRefCntPtr<X> X3;
IntrusiveRefCntPtr<X> X4;
X3 = std::move(Y3);
X4 = Y4;
EXPECT_EQ(P1, X1.get());
EXPECT_EQ(P2, X2.get());
EXPECT_EQ(P3, X3.get());
EXPECT_EQ(P4, X4.get());
}
} // end namespace llvm