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