From 4d0003765d92a9e90649c6bd28b137c2243b79a8 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Wed, 4 Mar 2026 12:56:25 -0800 Subject: [PATCH] refactors `LookupCppImpl` to handle multiple associated functions (#6816) `LookupCppImpl` is used to find associated functions for a witness. As some witnesses contain multiple associated functions, we need robust mechanims for looking up C++ components. The logic in `LookupCppImpl` is primarily concerned with finding exactly one C++ declaration at a time. In order to handle witnesses with more than one associated function, we move the bulk of `LookupCppImpl` to a new function called `FindCppAssociatedFunction`. This frees up `CppLookupImpl` to delegate to `FindCppAssociatedFunction` when a witness has only one associated function, and to functions that are able to compose multiple associated functions. --------- Co-authored-by: Geoff Romer --- toolchain/check/cpp/impl_lookup.cpp | 102 +++++++++++++++++++++------- toolchain/check/custom_witness.cpp | 3 + toolchain/check/custom_witness.h | 8 +-- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/toolchain/check/cpp/impl_lookup.cpp b/toolchain/check/cpp/impl_lookup.cpp index 3aba57c009c8..cc85a0ba461f 100644 --- a/toolchain/check/cpp/impl_lookup.cpp +++ b/toolchain/check/cpp/impl_lookup.cpp @@ -4,6 +4,8 @@ #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" @@ -57,41 +59,55 @@ struct DeclInfo { }; } // namespace -// 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, - CoreInterface core_interface, - clang::CXXRecordDecl* class_decl) -> DeclInfo { - // TODO: Handle other interfaces. +// 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 {.decl = clang_sema.LookupCopyingConstructor( - class_decl, clang::Qualifiers::Const), - .signature = {.num_params = 1}}; + return {llvm::to_underlying(AssociatedFunction::CopyConstructor)}; case CoreInterface::Destroy: - return {.decl = clang_sema.LookupDestructor(class_decl), - .signature = {.num_params = 0}}; + return {llvm::to_underlying(AssociatedFunction::Destructor)}; case CoreInterface::Unknown: - CARBON_FATAL("shouldn't be called with `Unknown`"); + CARBON_FATAL( + "`CoreInterface::Unknown` doesn't have a `CppCoreFunction` mapping"); } } -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 { +// 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* class_decl = TypeAsClassDecl(context, query_self_const_id); - if (!class_decl) { - return SemIR::InstId::None; - } - - auto decl_info = - GetDeclForCoreInterface(context.clang_sema(), core_interface, class_decl); + 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. @@ -113,13 +129,47 @@ auto LookupCppImpl(Context& context, SemIR::LocId loc_id, 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 associated_functions = GetCppAssociatedFunctions(core_interface); + auto witness_id = SemIR::ErrorInst::InstId; + + switch (core_interface) { + case CoreInterface::Copy: + case CoreInterface::Destroy: { + CARBON_CHECK(associated_functions.count() == 1); + witness_id = FindCppAssociatedFunction( + context, loc_id, + static_cast(associated_functions.to_ullong()), + class_decl); + } break; + 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, {fn_id}); + query_specific_interface_id, {witness_id}); } } // namespace Carbon::Check diff --git a/toolchain/check/custom_witness.cpp b/toolchain/check/custom_witness.cpp index 354f6ddff940..6adf28a26b1f 100644 --- a/toolchain/check/custom_witness.cpp +++ b/toolchain/check/custom_witness.cpp @@ -209,6 +209,9 @@ auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, // Fill in the witness table. for (const auto& [assoc_entity_id, value_id] : llvm::zip_equal(assoc_entities, values)) { + CARBON_DCHECK(value_id != SemIR::InstId::None); + CARBON_DCHECK(value_id != SemIR::ErrorInst::InstId); + LoadImportRef(context, assoc_entity_id); // Build a witness with the current contents of the witness table. This will diff --git a/toolchain/check/custom_witness.h b/toolchain/check/custom_witness.h index 2119b7da86bc..68846f045db3 100644 --- a/toolchain/check/custom_witness.h +++ b/toolchain/check/custom_witness.h @@ -21,11 +21,11 @@ auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, // Significant interfaces in `Core` which correspond to language features and // can have custom witnesses. -enum class CoreInterface { - Copy, - Destroy, +enum class CoreInterface : std::int8_t { + Copy = 1 << 0, + Destroy = 1 << 1, - Unknown, + Unknown = -1, }; // Given an interface, returns the corresponding enum if it's covered by