// Part of the Carbon Language 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 "toolchain/check/cpp/impl_lookup.h" #include #include "clang/Sema/Sema.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/overload_resolution.h" #include "toolchain/check/custom_witness.h" #include "toolchain/check/impl.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // If the given type is a C++ class type, returns the corresponding class // declaration. Otherwise returns nullptr. // TODO: Handle qualified types. static auto TypeAsClassDecl(Context& context, SemIR::ConstantId query_self_const_id) -> clang::CXXRecordDecl* { auto self_inst_id = context.constant_values().GetInstId(query_self_const_id); auto class_type = context.insts().TryGetAs(self_inst_id); if (!class_type) { // Not a class. return nullptr; } SemIR::NameScopeId class_scope_id = context.classes().Get(class_type->class_id).scope_id; if (!class_scope_id.has_value()) { return nullptr; } const auto& scope = context.name_scopes().Get(class_scope_id); auto decl_id = scope.clang_decl_context_id(); if (!decl_id.has_value()) { return nullptr; } return dyn_cast( context.clang_decls().Get(decl_id).key.decl); } namespace { // See `GetDeclForCoreInterface`. struct DeclInfo { clang::NamedDecl* decl; SemIR::ClangDeclKey::Signature signature; }; } // namespace // Describes the function that needs to be looked up. enum class AssociatedFunction : std::underlying_type_t { // CoreInterface::Copy CopyConstructor = llvm::to_underlying(CoreInterface::Copy), // CoreInterface::Destroy Destructor = llvm::to_underlying(CoreInterface::Destroy), }; // Maps a `CoreInterface` to its corresponding set of `CppCoreFunction`s. static auto GetCppAssociatedFunctions(const CoreInterface core_interface) -> std::bitset<8> { switch (core_interface) { case CoreInterface::Copy: return {llvm::to_underlying(AssociatedFunction::CopyConstructor)}; case CoreInterface::Destroy: return {llvm::to_underlying(AssociatedFunction::Destructor)}; case CoreInterface::Unknown: case CoreInterface::IntFitsIn: CARBON_FATAL("No AssociatedFunction mapping for this interface"); } } // Retrieves a `core_interface`'s corresponding `NamedDecl`, also with the // expected number of parameters. May return a null decl. auto GetDeclForCoreInterface(clang::Sema& clang_sema, AssociatedFunction associated_function, clang::CXXRecordDecl* class_decl) -> DeclInfo { // TODO: Handle other interfaces. switch (associated_function) { case AssociatedFunction::CopyConstructor: return {.decl = clang_sema.LookupCopyingConstructor( class_decl, clang::Qualifiers::Const), .signature = {.num_params = 1}}; case AssociatedFunction::Destructor: return {.decl = clang_sema.LookupDestructor(class_decl), .signature = {.num_params = 0}}; } } static auto FindCppAssociatedFunction(Context& context, SemIR::LocId loc_id, AssociatedFunction associated_function, clang::CXXRecordDecl* class_decl) -> SemIR::InstId { // TODO: This should provide `Destroy` for enums and other trivially // destructible types. auto decl_info = GetDeclForCoreInterface(context.clang_sema(), associated_function, class_decl); if (!decl_info.decl) { // TODO: If the impl lookup failure is an error, we should produce a // diagnostic explaining why the class is not copyable/destructible. return SemIR::InstId::None; } auto* cpp_fn = cast(decl_info.decl); if (context.clang_sema().DiagnoseUseOfOverloadedDecl( cpp_fn, GetCppLocation(context, loc_id))) { return SemIR::ErrorInst::InstId; } auto fn_id = ImportCppFunctionDecl(context, loc_id, cpp_fn, decl_info.signature); if (fn_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } CheckCppOverloadAccess( context, loc_id, clang::DeclAccessPair::make(cpp_fn, cpp_fn->getAccess()), context.insts().GetAsKnownInstId(fn_id)); return fn_id; } auto LookupCppImpl(Context& context, SemIR::LocId loc_id, CoreInterface core_interface, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, const TypeStructure* best_impl_type_structure, SemIR::LocId best_impl_loc_id) -> SemIR::InstId { auto* class_decl = TypeAsClassDecl(context, query_self_const_id); if (!class_decl) { return SemIR::InstId::None; } auto witness_id = SemIR::ErrorInst::InstId; switch (core_interface) { case CoreInterface::Copy: case CoreInterface::Destroy: { auto associated_functions = GetCppAssociatedFunctions(core_interface); CARBON_CHECK(associated_functions.count() == 1); witness_id = FindCppAssociatedFunction( context, loc_id, static_cast(associated_functions.to_ullong()), class_decl); } break; case CoreInterface::IntFitsIn: return SemIR::InstId::None; case CoreInterface::Unknown: CARBON_FATAL("shouldn't be called with `Unknown`"); } if (witness_id == SemIR::InstId::None || witness_id == SemIR::ErrorInst::InstId) { return witness_id; } // TODO: Infer a C++ type structure and check whether it's less strict than // the best Carbon type structure. static_cast(best_impl_type_structure); static_cast(best_impl_loc_id); return BuildCustomWitness(context, loc_id, query_self_const_id, query_specific_interface_id, {witness_id}); } } // namespace Carbon::Check