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