blob: d1e0504f239a48cf94ee9e0795bf038836cbe8ea [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/base/nullability.h"
#include "absl/functional/function_ref.h"
#include "absl/log/check.h"
#include "rs_bindings_from_cc/ast_util.h"
#include "rs_bindings_from_cc/decl_importer.h"
#include "rs_bindings_from_cc/ir.h"
#include "rs_bindings_from_cc/recording_diagnostic_consumer.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Sema/Sema.h"
namespace crubit {
namespace {
// Returns a copy constructor for `record`, or `nullptr` if none is declared.
//
// Does not traverse to the base classes.
static clang::CXXConstructorDecl* GetCopyCtor(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 clang::CXXConstructorDecl* GetMoveCtor(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(
clang::CXXRecordDecl* record,
absl::FunctionRef<clang::CXXMethodDecl*(clang::CXXRecordDecl*)> getter) {
auto nonrecursive_has_only_defaulted =
[&getter](const clang::CXXRecordDecl* record) {
const clang::CXXMethodDecl* decl =
getter(const_cast<clang::CXXRecordDecl*>(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(
ImportContext* absl_nullable ictx, clang::RecordDecl& record_decl,
absl::FunctionRef<clang::CXXMethodDecl*(clang::CXXRecordDecl*)> getter) {
auto* cxx_record_decl = clang::dyn_cast<clang::CXXRecordDecl>(&record_decl);
if (cxx_record_decl == nullptr) {
return SpecialMemberFunc::kTrivial;
}
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 (auto* ctor = clang::dyn_cast<clang::CXXConstructorDecl>(decl);
ctor != nullptr && ctor->isDefaulted() && !ctor->isDefaultConstructor() &&
!ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted() &&
ictx != nullptr) {
// Alternate options that don't seem to work include
// Sema::ShouldDeleteSpecialMember and
// Sema::ForceDeclarationOfImplicitMembers. These might be cheaper, but it
// appears that we need to fully synthesize the special member functions
// and the templates they use to catch any possible errors.
crubit::RecordingDiagnosticConsumer diagnostic_recorder =
crubit::RecordDiagnostics(ictx->sema_.getDiagnostics(), [&] {
auto* mutable_ctor = const_cast<clang::CXXConstructorDecl*>(ctor);
FakeTUScope fake_tu_scope(*ictx);
clang::Sema::SynthesizedFunctionScope synthesized_function_scope(
ictx->sema_, mutable_ctor);
// We can't use DefineImplicitDefaultConstructor directly because
// mutable_ctor is *not* a default ctor (it's *defaulted*).
// DefineImplicitDefaultConstructor eventually calls to
// SetCtorInitializers, which in turn will produce diagnostics if
// the defaulted ctor is impossible.
ictx->sema_.SetCtorInitializers(mutable_ctor, false);
});
if (diagnostic_recorder.getNumErrors() != 0) {
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(ImportContext& ictx,
clang::RecordDecl& record_decl) {
return GetSpecialMemberFunc(&ictx, record_decl, &GetCopyCtor);
}
SpecialMemberFunc GetMoveCtorSpecialMemberFunc(ImportContext& ictx,
clang::RecordDecl& record_decl) {
return GetSpecialMemberFunc(&ictx, record_decl, &GetMoveCtor);
}
SpecialMemberFunc GetDestructorSpecialMemberFunc(
clang::RecordDecl& record_decl) {
return GetSpecialMemberFunc(nullptr, record_decl,
[](auto c) { return c->getDestructor(); });
}
} // namespace crubit