//=======- 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