| //=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "PtrTypesSemantics.h" |
| #include "ASTUtils.h" |
| #include "clang/AST/CXXInheritance.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "llvm/ADT/Optional.h" |
| |
| using llvm::Optional; |
| using namespace clang; |
| |
| namespace { |
| |
| bool hasPublicRefAndDeref(const CXXRecordDecl *R) { |
| assert(R); |
| assert(R->hasDefinition()); |
| |
| bool hasRef = false; |
| bool hasDeref = false; |
| for (const CXXMethodDecl *MD : R->methods()) { |
| const auto MethodName = safeGetName(MD); |
| |
| if (MethodName == "ref" && MD->getAccess() == AS_public) { |
| if (hasDeref) |
| return true; |
| hasRef = true; |
| } else if (MethodName == "deref" && MD->getAccess() == AS_public) { |
| if (hasRef) |
| return true; |
| hasDeref = true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| namespace clang { |
| |
| llvm::Optional<const clang::CXXRecordDecl *> |
| isRefCountable(const CXXBaseSpecifier *Base) { |
| assert(Base); |
| |
| const Type *T = Base->getType().getTypePtrOrNull(); |
| if (!T) |
| return llvm::None; |
| |
| const CXXRecordDecl *R = T->getAsCXXRecordDecl(); |
| if (!R) |
| return llvm::None; |
| if (!R->hasDefinition()) |
| return llvm::None; |
| |
| return hasPublicRefAndDeref(R) ? R : nullptr; |
| } |
| |
| llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) { |
| assert(R); |
| |
| R = R->getDefinition(); |
| if (!R) |
| return llvm::None; |
| |
| if (hasPublicRefAndDeref(R)) |
| return true; |
| |
| CXXBasePaths Paths; |
| Paths.setOrigin(const_cast<CXXRecordDecl *>(R)); |
| |
| bool AnyInconclusiveBase = false; |
| const auto isRefCountableBase = |
| [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { |
| Optional<const clang::CXXRecordDecl *> IsRefCountable = |
| clang::isRefCountable(Base); |
| if (!IsRefCountable) { |
| AnyInconclusiveBase = true; |
| return false; |
| } |
| return (*IsRefCountable) != nullptr; |
| }; |
| |
| bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, |
| /*LookupInDependent =*/true); |
| if (AnyInconclusiveBase) |
| return llvm::None; |
| |
| return BasesResult; |
| } |
| |
| bool isCtorOfRefCounted(const clang::FunctionDecl *F) { |
| assert(F); |
| const auto &FunctionName = safeGetName(F); |
| |
| return FunctionName == "Ref" || FunctionName == "makeRef" |
| |
| || FunctionName == "RefPtr" || FunctionName == "makeRefPtr" |
| |
| || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" || |
| FunctionName == "makeUniqueRefWithoutFastMallocCheck" |
| |
| || FunctionName == "String" || FunctionName == "AtomString" || |
| FunctionName == "UniqueString" |
| // FIXME: Implement as attribute. |
| || FunctionName == "Identifier"; |
| } |
| |
| llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) { |
| // Keep isRefCounted first as it's cheaper. |
| if (isRefCounted(Class)) |
| return false; |
| |
| llvm::Optional<bool> IsRefCountable = isRefCountable(Class); |
| if (!IsRefCountable) |
| return llvm::None; |
| |
| return (*IsRefCountable); |
| } |
| |
| llvm::Optional<bool> isUncountedPtr(const Type *T) { |
| assert(T); |
| |
| if (T->isPointerType() || T->isReferenceType()) { |
| if (auto *CXXRD = T->getPointeeCXXRecordDecl()) { |
| return isUncounted(CXXRD); |
| } |
| } |
| return false; |
| } |
| |
| Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) { |
| assert(M); |
| |
| if (isa<CXXMethodDecl>(M)) { |
| const CXXRecordDecl *calleeMethodsClass = M->getParent(); |
| auto className = safeGetName(calleeMethodsClass); |
| auto methodName = safeGetName(M); |
| |
| if (((className == "Ref" || className == "RefPtr") && |
| methodName == "get") || |
| ((className == "String" || className == "AtomString" || |
| className == "AtomStringImpl" || className == "UniqueString" || |
| className == "UniqueStringImpl" || className == "Identifier") && |
| methodName == "impl")) |
| return true; |
| |
| // Ref<T> -> T conversion |
| // FIXME: Currently allowing any Ref<T> -> whatever cast. |
| if (className == "Ref" || className == "RefPtr") { |
| if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) { |
| if (auto *targetConversionType = |
| maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) { |
| return isUncountedPtr(targetConversionType); |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool isRefCounted(const CXXRecordDecl *R) { |
| assert(R); |
| if (auto *TmplR = R->getTemplateInstantiationPattern()) { |
| // FIXME: String/AtomString/UniqueString |
| const auto &ClassName = safeGetName(TmplR); |
| return ClassName == "RefPtr" || ClassName == "Ref"; |
| } |
| return false; |
| } |
| |
| bool isPtrConversion(const FunctionDecl *F) { |
| assert(F); |
| if (isCtorOfRefCounted(F)) |
| return true; |
| |
| // FIXME: check # of params == 1 |
| const auto FunctionName = safeGetName(F); |
| if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || |
| FunctionName == "makeWeakPtr" |
| |
| || FunctionName == "downcast" || FunctionName == "bitwise_cast") |
| return true; |
| |
| return false; |
| } |
| |
| } // namespace clang |