1 //===- unittest/ADT/IntrusiveRefCntPtrTest.cpp ----------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/ADT/IntrusiveRefCntPtr.h" 10 #include "gtest/gtest.h" 11 12 namespace llvm { 13 14 namespace { 15 int NumInstances = 0; 16 template <template <typename> class Base> 17 struct SimpleRefCounted : Base<SimpleRefCounted<Base>> { 18 SimpleRefCounted() { ++NumInstances; } 19 SimpleRefCounted(const SimpleRefCounted &RHS) : Base<SimpleRefCounted>(RHS) { 20 ++NumInstances; 21 } 22 ~SimpleRefCounted() { --NumInstances; } 23 }; 24 } // anonymous namespace 25 26 template <typename T> struct IntrusiveRefCntPtrTest : testing::Test {}; 27 28 typedef ::testing::Types<SimpleRefCounted<RefCountedBase>, 29 SimpleRefCounted<ThreadSafeRefCountedBase>> 30 IntrusiveRefCntTypes; 31 TYPED_TEST_SUITE(IntrusiveRefCntPtrTest, IntrusiveRefCntTypes, ); 32 33 TYPED_TEST(IntrusiveRefCntPtrTest, RefCountedBaseCopyDoesNotLeak) { 34 EXPECT_EQ(0, NumInstances); 35 { 36 TypeParam *S1 = new TypeParam; 37 IntrusiveRefCntPtr<TypeParam> R1 = S1; 38 TypeParam *S2 = new TypeParam(*S1); 39 IntrusiveRefCntPtr<TypeParam> R2 = S2; 40 EXPECT_EQ(2, NumInstances); 41 } 42 EXPECT_EQ(0, NumInstances); 43 } 44 45 TYPED_TEST(IntrusiveRefCntPtrTest, InteropsWithUniquePtr) { 46 EXPECT_EQ(0, NumInstances); 47 { 48 auto S1 = std::make_unique<TypeParam>(); 49 IntrusiveRefCntPtr<TypeParam> R1 = std::move(S1); 50 EXPECT_EQ(1, NumInstances); 51 EXPECT_EQ(S1, nullptr); 52 } 53 EXPECT_EQ(0, NumInstances); 54 } 55 56 TYPED_TEST(IntrusiveRefCntPtrTest, MakeIntrusiveRefCnt) { 57 EXPECT_EQ(0, NumInstances); 58 { 59 auto S1 = makeIntrusiveRefCnt<TypeParam>(); 60 auto S2 = makeIntrusiveRefCnt<const TypeParam>(); 61 EXPECT_EQ(2, NumInstances); 62 static_assert( 63 std::is_same<decltype(S1), IntrusiveRefCntPtr<TypeParam>>::value, 64 "Non-const type mismatch"); 65 static_assert( 66 std::is_same<decltype(S2), IntrusiveRefCntPtr<const TypeParam>>::value, 67 "Const type mismatch"); 68 } 69 EXPECT_EQ(0, NumInstances); 70 } 71 72 struct InterceptRefCounted : public RefCountedBase<InterceptRefCounted> { 73 InterceptRefCounted(bool *Released, bool *Retained) 74 : Released(Released), Retained(Retained) {} 75 bool * const Released; 76 bool * const Retained; 77 }; 78 template <> struct IntrusiveRefCntPtrInfo<InterceptRefCounted> { 79 static void retain(InterceptRefCounted *I) { 80 *I->Retained = true; 81 I->Retain(); 82 } 83 static void release(InterceptRefCounted *I) { 84 *I->Released = true; 85 I->Release(); 86 } 87 }; 88 TEST(IntrusiveRefCntPtr, UsesTraitsToRetainAndRelease) { 89 bool Released = false; 90 bool Retained = false; 91 { 92 InterceptRefCounted *I = new InterceptRefCounted(&Released, &Retained); 93 IntrusiveRefCntPtr<InterceptRefCounted> R = I; 94 } 95 EXPECT_TRUE(Released); 96 EXPECT_TRUE(Retained); 97 } 98 99 // Test that the generic constructors use SFINAE to disable invalid 100 // conversions. 101 struct X : RefCountedBase<X> {}; 102 struct Y : X {}; 103 struct Z : RefCountedBase<Z> {}; 104 static_assert(!std::is_convertible<IntrusiveRefCntPtr<X> &&, 105 IntrusiveRefCntPtr<Y>>::value, 106 "X&& -> Y should be rejected with SFINAE"); 107 static_assert(!std::is_convertible<const IntrusiveRefCntPtr<X> &, 108 IntrusiveRefCntPtr<Y>>::value, 109 "const X& -> Y should be rejected with SFINAE"); 110 static_assert( 111 !std::is_convertible<std::unique_ptr<X>, IntrusiveRefCntPtr<Y>>::value, 112 "X -> Y should be rejected with SFINAE"); 113 static_assert(!std::is_convertible<IntrusiveRefCntPtr<X> &&, 114 IntrusiveRefCntPtr<Z>>::value, 115 "X&& -> Z should be rejected with SFINAE"); 116 static_assert(!std::is_convertible<const IntrusiveRefCntPtr<X> &, 117 IntrusiveRefCntPtr<Z>>::value, 118 "const X& -> Z should be rejected with SFINAE"); 119 static_assert( 120 !std::is_convertible<std::unique_ptr<X>, IntrusiveRefCntPtr<Z>>::value, 121 "X -> Z should be rejected with SFINAE"); 122 123 TEST(IntrusiveRefCntPtr, InteropsWithConvertible) { 124 // Check converting constructors and operator=. 125 auto Y1 = makeIntrusiveRefCnt<Y>(); 126 auto Y2 = makeIntrusiveRefCnt<Y>(); 127 auto Y3 = makeIntrusiveRefCnt<Y>(); 128 auto Y4 = makeIntrusiveRefCnt<Y>(); 129 const void *P1 = Y1.get(); 130 const void *P2 = Y2.get(); 131 const void *P3 = Y3.get(); 132 const void *P4 = Y4.get(); 133 IntrusiveRefCntPtr<X> X1 = std::move(Y1); 134 IntrusiveRefCntPtr<X> X2 = Y2; 135 IntrusiveRefCntPtr<X> X3; 136 IntrusiveRefCntPtr<X> X4; 137 X3 = std::move(Y3); 138 X4 = Y4; 139 EXPECT_EQ(P1, X1.get()); 140 EXPECT_EQ(P2, X2.get()); 141 EXPECT_EQ(P3, X3.get()); 142 EXPECT_EQ(P4, X4.get()); 143 } 144 145 } // end namespace llvm 146