1 //=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==// 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 "PtrTypesSemantics.h" 10 #include "ASTUtils.h" 11 #include "clang/AST/CXXInheritance.h" 12 #include "clang/AST/Decl.h" 13 #include "clang/AST/DeclCXX.h" 14 #include "clang/AST/ExprCXX.h" 15 #include <optional> 16 17 using namespace clang; 18 19 namespace { 20 21 bool hasPublicRefAndDeref(const CXXRecordDecl *R) { 22 assert(R); 23 assert(R->hasDefinition()); 24 25 bool hasRef = false; 26 bool hasDeref = false; 27 for (const CXXMethodDecl *MD : R->methods()) { 28 const auto MethodName = safeGetName(MD); 29 30 if (MethodName == "ref" && MD->getAccess() == AS_public) { 31 if (hasDeref) 32 return true; 33 hasRef = true; 34 } else if (MethodName == "deref" && MD->getAccess() == AS_public) { 35 if (hasRef) 36 return true; 37 hasDeref = true; 38 } 39 } 40 return false; 41 } 42 43 } // namespace 44 45 namespace clang { 46 47 std::optional<const clang::CXXRecordDecl*> 48 isRefCountable(const CXXBaseSpecifier* Base) 49 { 50 assert(Base); 51 52 const Type *T = Base->getType().getTypePtrOrNull(); 53 if (!T) 54 return std::nullopt; 55 56 const CXXRecordDecl *R = T->getAsCXXRecordDecl(); 57 if (!R) 58 return std::nullopt; 59 if (!R->hasDefinition()) 60 return std::nullopt; 61 62 return hasPublicRefAndDeref(R) ? R : nullptr; 63 } 64 65 std::optional<bool> isRefCountable(const CXXRecordDecl* R) 66 { 67 assert(R); 68 69 R = R->getDefinition(); 70 if (!R) 71 return std::nullopt; 72 73 if (hasPublicRefAndDeref(R)) 74 return true; 75 76 CXXBasePaths Paths; 77 Paths.setOrigin(const_cast<CXXRecordDecl *>(R)); 78 79 bool AnyInconclusiveBase = false; 80 const auto isRefCountableBase = 81 [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { 82 std::optional<const clang::CXXRecordDecl*> IsRefCountable = clang::isRefCountable(Base); 83 if (!IsRefCountable) { 84 AnyInconclusiveBase = true; 85 return false; 86 } 87 return (*IsRefCountable) != nullptr; 88 }; 89 90 bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, 91 /*LookupInDependent =*/true); 92 if (AnyInconclusiveBase) 93 return std::nullopt; 94 95 return BasesResult; 96 } 97 98 bool isCtorOfRefCounted(const clang::FunctionDecl *F) { 99 assert(F); 100 const auto &FunctionName = safeGetName(F); 101 102 return FunctionName == "Ref" || FunctionName == "makeRef" 103 104 || FunctionName == "RefPtr" || FunctionName == "makeRefPtr" 105 106 || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" || 107 FunctionName == "makeUniqueRefWithoutFastMallocCheck" 108 109 || FunctionName == "String" || FunctionName == "AtomString" || 110 FunctionName == "UniqueString" 111 // FIXME: Implement as attribute. 112 || FunctionName == "Identifier"; 113 } 114 115 std::optional<bool> isUncounted(const CXXRecordDecl* Class) 116 { 117 // Keep isRefCounted first as it's cheaper. 118 if (isRefCounted(Class)) 119 return false; 120 121 std::optional<bool> IsRefCountable = isRefCountable(Class); 122 if (!IsRefCountable) 123 return std::nullopt; 124 125 return (*IsRefCountable); 126 } 127 128 std::optional<bool> isUncountedPtr(const Type* T) 129 { 130 assert(T); 131 132 if (T->isPointerType() || T->isReferenceType()) { 133 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) { 134 return isUncounted(CXXRD); 135 } 136 } 137 return false; 138 } 139 140 std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M) 141 { 142 assert(M); 143 144 if (isa<CXXMethodDecl>(M)) { 145 const CXXRecordDecl *calleeMethodsClass = M->getParent(); 146 auto className = safeGetName(calleeMethodsClass); 147 auto methodName = safeGetName(M); 148 149 if (((className == "Ref" || className == "RefPtr") && 150 methodName == "get") || 151 ((className == "String" || className == "AtomString" || 152 className == "AtomStringImpl" || className == "UniqueString" || 153 className == "UniqueStringImpl" || className == "Identifier") && 154 methodName == "impl")) 155 return true; 156 157 // Ref<T> -> T conversion 158 // FIXME: Currently allowing any Ref<T> -> whatever cast. 159 if (className == "Ref" || className == "RefPtr") { 160 if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) { 161 if (auto *targetConversionType = 162 maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) { 163 return isUncountedPtr(targetConversionType); 164 } 165 } 166 } 167 } 168 return false; 169 } 170 171 bool isRefCounted(const CXXRecordDecl *R) { 172 assert(R); 173 if (auto *TmplR = R->getTemplateInstantiationPattern()) { 174 // FIXME: String/AtomString/UniqueString 175 const auto &ClassName = safeGetName(TmplR); 176 return ClassName == "RefPtr" || ClassName == "Ref"; 177 } 178 return false; 179 } 180 181 bool isPtrConversion(const FunctionDecl *F) { 182 assert(F); 183 if (isCtorOfRefCounted(F)) 184 return true; 185 186 // FIXME: check # of params == 1 187 const auto FunctionName = safeGetName(F); 188 if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || 189 FunctionName == "makeWeakPtr" 190 191 || FunctionName == "downcast" || FunctionName == "bitwise_cast") 192 return true; 193 194 return false; 195 } 196 197 } // namespace clang 198