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