From 655932da0b2a208dfa77c4b39d73b62ecf152942 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Wed, 17 Dec 2025 13:36:44 -0800 Subject: [PATCH] Refactor BuildCustomWitness out to its own file (#6515) Per request at https://github.com/carbon-language/carbon-lang/pull/6512#discussion_r2627265838 --- toolchain/check/BUILD | 2 + toolchain/check/cpp/impl_lookup.cpp | 1 + toolchain/check/custom_witness.cpp | 94 +++++++++++++++++++++++++++++ toolchain/check/custom_witness.h | 24 ++++++++ toolchain/check/impl.cpp | 79 ------------------------ 5 files changed, 121 insertions(+), 79 deletions(-) create mode 100644 toolchain/check/custom_witness.cpp create mode 100644 toolchain/check/custom_witness.h diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index e09c342d2573..cf8eed6f0ff1 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -34,6 +34,7 @@ cc_library( "cpp/overload_resolution.cpp", "cpp/thunk.cpp", "cpp/type_mapping.cpp", + "custom_witness.cpp", "decl_name_stack.cpp", "deduce.cpp", "deferred_definition_worklist.cpp", @@ -91,6 +92,7 @@ cc_library( "cpp/overload_resolution.h", "cpp/thunk.h", "cpp/type_mapping.h", + "custom_witness.h", "decl_introducer_state.h", "decl_name_stack.h", "deduce.h", diff --git a/toolchain/check/cpp/impl_lookup.cpp b/toolchain/check/cpp/impl_lookup.cpp index 3c24f4dd3914..227c227ba276 100644 --- a/toolchain/check/cpp/impl_lookup.cpp +++ b/toolchain/check/cpp/impl_lookup.cpp @@ -9,6 +9,7 @@ #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" diff --git a/toolchain/check/custom_witness.cpp b/toolchain/check/custom_witness.cpp new file mode 100644 index 000000000000..affa9e1cfbc2 --- /dev/null +++ b/toolchain/check/custom_witness.cpp @@ -0,0 +1,94 @@ +// 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/custom_witness.h" + +#include "toolchain/base/kind_switch.h" +#include "toolchain/check/impl.h" +#include "toolchain/check/import_ref.h" +#include "toolchain/check/inst.h" +#include "toolchain/check/type.h" + +namespace Carbon::Check { + +auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, + SemIR::TypeId self_type_id, + SemIR::SpecificInterface specific_interface, + llvm::ArrayRef values) -> SemIR::InstId { + const auto& interface = + context.interfaces().Get(specific_interface.interface_id); + auto assoc_entities = + context.inst_blocks().GetOrEmpty(interface.associated_entities_id); + if (assoc_entities.size() != values.size()) { + context.TODO(loc_id, ("Unsupported definition of interface " + + context.names().GetFormatted(interface.name_id)) + .str()); + return SemIR::ErrorInst::InstId; + } + + llvm::SmallVector entries; + + // Build a witness with the current contents of the witness table. This will + // grow as we progress through the impl. In theory this will build O(n^2) + // table entries, but in practice n <= 2, so that's OK. + // + // This is necessary because later associated entities may refer to earlier + // associated entities in their signatures. In particular, an associated + // result type may be used as the return type of an associated function. + // + // TODO: Consider building one witness after all associated constants, and + // then a second after all associated functions, rather than building one at + // each step. For now this doesn't really matter since we don't have more than + // one of each anyway. + auto make_witness = [&] { + return context.constant_values().GetInstId( + EvalOrAddInst( + context, loc_id, + {.type_id = + GetSingletonType(context, SemIR::WitnessType::TypeInstId), + .elements_id = context.inst_blocks().Add(entries)})); + }; + + // Fill in the witness table. + for (const auto& [assoc_entity_id, value_id] : + llvm::zip_equal(assoc_entities, values)) { + LoadImportRef(context, assoc_entity_id); + auto decl_id = + context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific( + context.sem_ir(), specific_interface.specific_id, assoc_entity_id)); + CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); + auto decl = context.insts().Get(decl_id); + CARBON_KIND_SWITCH(decl) { + case CARBON_KIND(SemIR::StructValue struct_value): { + if (struct_value.type_id == SemIR::ErrorInst::TypeId) { + return SemIR::ErrorInst::InstId; + } + // TODO: If a thunk is needed, this will build a different value each + // time it's called, so we won't properly deduplicate repeated + // witnesses. + // TODO: Skip calling make_witness if this function signature doesn't + // involve `Self`. + entries.push_back(CheckAssociatedFunctionImplementation( + context, + context.types().GetAs(struct_value.type_id), + value_id, self_type_id, make_witness(), + /*defer_thunk_definition=*/false)); + break; + } + case SemIR::AssociatedConstantDecl::Kind: { + context.TODO(loc_id, + "Associated constant in interface with synthesized impl"); + return SemIR::ErrorInst::InstId; + } + default: + CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId, + "Unexpected kind of associated entity {0}", decl); + return SemIR::ErrorInst::InstId; + } + } + + return make_witness(); +} + +} // namespace Carbon::Check diff --git a/toolchain/check/custom_witness.h b/toolchain/check/custom_witness.h new file mode 100644 index 000000000000..a2afed9da3ba --- /dev/null +++ b/toolchain/check/custom_witness.h @@ -0,0 +1,24 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_ +#define CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_ + +#include "toolchain/check/context.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::Check { + +// Builds a witness that the given type implements the given interface, +// populating it with the specified set of values. Returns a corresponding +// lookup result. Produces a diagnostic and returns `None` if the specified +// values aren't suitable for the interface. +auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, + SemIR::TypeId self_type_id, + SemIR::SpecificInterface specific_interface, + llvm::ArrayRef values) -> SemIR::InstId; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_ diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 89e798d602d1..858adab53333 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -724,83 +724,4 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, return identified.impl_as_target_interface(); } -auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, - SemIR::TypeId self_type_id, - SemIR::SpecificInterface specific_interface, - llvm::ArrayRef values) -> SemIR::InstId { - const auto& interface = - context.interfaces().Get(specific_interface.interface_id); - auto assoc_entities = - context.inst_blocks().GetOrEmpty(interface.associated_entities_id); - if (assoc_entities.size() != values.size()) { - context.TODO(loc_id, ("Unsupported definition of interface " + - context.names().GetFormatted(interface.name_id)) - .str()); - return SemIR::ErrorInst::InstId; - } - - llvm::SmallVector entries; - - // Build a witness with the current contents of the witness table. This will - // grow as we progress through the impl. In theory this will build O(n^2) - // table entries, but in practice n <= 2, so that's OK. - // - // This is necessary because later associated entities may refer to earlier - // associated entities in their signatures. In particular, an associated - // result type may be used as the return type of an associated function. - // - // TODO: Consider building one witness after all associated constants, and - // then a second after all associated functions, rather than building one at - // each step. For now this doesn't really matter since we don't have more than - // one of each anyway. - auto make_witness = [&] { - return context.constant_values().GetInstId( - EvalOrAddInst( - context, loc_id, - {.type_id = - GetSingletonType(context, SemIR::WitnessType::TypeInstId), - .elements_id = context.inst_blocks().Add(entries)})); - }; - - // Fill in the witness table. - for (const auto& [assoc_entity_id, value_id] : - llvm::zip_equal(assoc_entities, values)) { - LoadImportRef(context, assoc_entity_id); - auto decl_id = - context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific( - context.sem_ir(), specific_interface.specific_id, assoc_entity_id)); - CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); - auto decl = context.insts().Get(decl_id); - CARBON_KIND_SWITCH(decl) { - case CARBON_KIND(SemIR::StructValue struct_value): { - if (struct_value.type_id == SemIR::ErrorInst::TypeId) { - return SemIR::ErrorInst::InstId; - } - // TODO: If a thunk is needed, this will build a different value each - // time it's called, so we won't properly deduplicate repeated - // witnesses. - // TODO: Skip calling make_witness if this function signature doesn't - // involve `Self`. - entries.push_back(CheckAssociatedFunctionImplementation( - context, - context.types().GetAs(struct_value.type_id), - value_id, self_type_id, make_witness(), - /*defer_thunk_definition=*/false)); - break; - } - case SemIR::AssociatedConstantDecl::Kind: { - context.TODO(loc_id, - "Associated constant in interface with synthesized impl"); - return SemIR::ErrorInst::InstId; - } - default: - CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId, - "Unexpected kind of associated entity {0}", decl); - return SemIR::ErrorInst::InstId; - } - } - - return make_witness(); -} - } // namespace Carbon::Check