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