blob: f341864fc8f9027e59f401112f02765f880c276d [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "rs_bindings_from_cc/ast_convert.h"
#include "absl/functional/function_ref.h"
#include "absl/log/check.h"
#include "rs_bindings_from_cc/ir.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Specifiers.h"
namespace crubit {
namespace {
// Returns a copy constructor for `record`, or `nullptr` if none is declared.
//
// Does not traverse to the base classes.
static const clang::CXXConstructorDecl* GetCopyCtor(
const clang::CXXRecordDecl* record) {
for (clang::CXXConstructorDecl* ctor_decl : record->ctors()) {
if (ctor_decl->isCopyConstructor()) {
return ctor_decl;
}
}
return nullptr;
}
// Returns a move constructor for `record`, or `nullptr` if none is declared.
//
// Does not traverse to the base classes.
static const clang::CXXConstructorDecl* GetMoveCtor(
const clang::CXXRecordDecl* record) {
for (clang::CXXConstructorDecl* ctor_decl : record->ctors()) {
if (ctor_decl->isMoveConstructor()) {
return ctor_decl;
}
}
return nullptr;
}
// Returns true if this class, and all base classes, only define the specified
// special member implicitly or via =default, and only do so non-virtually.
//
// Args:
// record: the class/struct to check.
// getter: a function which returns the special member function in question.
// returns null if the special member function is implicitly defined.
bool HasNoUserProvidedSpecialMember(
const clang::CXXRecordDecl* record,
absl::FunctionRef<const clang::CXXMethodDecl*(const clang::CXXRecordDecl*)>
getter) {
auto nonrecursive_has_only_defaulted =
[&getter](const clang::CXXRecordDecl* record) {
const clang::CXXMethodDecl* decl = getter(record);
return decl == nullptr ||
(!decl->isUserProvided() && !decl->isVirtual());
};
if (!nonrecursive_has_only_defaulted(record)) {
return false;
}
return record->forallBases(nonrecursive_has_only_defaulted);
}
SpecialMemberFunc GetSpecialMemberFunc(
const clang::RecordDecl& record_decl,
absl::FunctionRef<const clang::CXXMethodDecl*(const clang::CXXRecordDecl*)>
getter) {
const auto* cxx_record_decl =
clang::dyn_cast<clang::CXXRecordDecl>(&record_decl);
if (cxx_record_decl == nullptr) {
return SpecialMemberFunc::kTrivial;
}
const clang::CXXMethodDecl* decl = getter(cxx_record_decl);
if (decl == nullptr) {
return SpecialMemberFunc::kUnavailable;
}
switch (decl->getAccess()) {
case clang::AS_public:
break;
case clang::AS_protected:
case clang::AS_private:
return SpecialMemberFunc::kUnavailable;
case clang::AS_none:
CHECK(false &&
"We should never be encoding a 'none' access specifier in IR.");
// We have to return something. kDeleted seems like a safe fallback.
return SpecialMemberFunc::kUnavailable;
}
if (decl->isDeleted()) {
return SpecialMemberFunc::kUnavailable;
} else if (decl->isTrivial()) {
return SpecialMemberFunc::kTrivial;
} else if (HasNoUserProvidedSpecialMember(cxx_record_decl, getter)) {
return SpecialMemberFunc::kNontrivialMembers;
} else {
return SpecialMemberFunc::kNontrivialUserDefined;
}
}
} // namespace
SpecialMemberFunc GetCopyCtorSpecialMemberFunc(
const clang::RecordDecl& record_decl) {
return GetSpecialMemberFunc(record_decl, &GetCopyCtor);
}
SpecialMemberFunc GetMoveCtorSpecialMemberFunc(
const clang::RecordDecl& record_decl) {
return GetSpecialMemberFunc(record_decl, &GetMoveCtor);
}
SpecialMemberFunc GetDestructorSpecialMemberFunc(
const clang::RecordDecl& record_decl) {
return GetSpecialMemberFunc(record_decl,
[](auto c) { return c->getDestructor(); });
}
} // namespace crubit