diff --git a/toolchain/check/cpp/impl_lookup.cpp b/toolchain/check/cpp/impl_lookup.cpp index 97b6c53854d9..3c24f4dd3914 100644 --- a/toolchain/check/cpp/impl_lookup.cpp +++ b/toolchain/check/cpp/impl_lookup.cpp @@ -46,88 +46,6 @@ static auto TypeAsClassDecl(Context& context, SemIR::TypeId type_id) context.clang_decls().Get(decl_id).key.decl); } -// 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. -static auto BuildWitness(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(); -} - static auto BuildSingleFunctionWitness( Context& context, SemIR::LocId loc_id, clang::FunctionDecl* cpp_fn, clang::DeclAccessPair found_decl, int num_params, @@ -144,8 +62,8 @@ static auto BuildSingleFunctionWitness( CARBON_CHECK(fn_id == SemIR::ErrorInst::InstId); return SemIR::ErrorInst::InstId; } - return BuildWitness(context, loc_id, self_type_id, specific_interface, - {fn_id}); + return BuildCustomWitness(context, loc_id, self_type_id, specific_interface, + {fn_id}); } static auto LookupCopyImpl(Context& context, SemIR::LocId loc_id, diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index a063cb8e8910..746badc8bd0e 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -6,6 +6,7 @@ #include +#include "toolchain/base/kind_switch.h" #include "toolchain/check/action.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/facet_type.h" @@ -318,103 +319,106 @@ auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::ImplWitnessAccess inst) -> ConstantEvalResult { CARBON_DIAGNOSTIC(ImplAccessMemberBeforeSet, Error, "accessing member from impl before it has a defined value"); - if (auto witness = - context.insts().TryGetAs(inst.witness_id)) { - // This is PerformAggregateAccess followed by GetConstantValueInSpecific. - auto witness_table = context.insts().GetAs( - witness->witness_table_id); - auto elements = context.inst_blocks().Get(witness_table.elements_id); - // `elements` can be empty if there is only a forward declaration of the - // impl. - if (!elements.empty()) { + CARBON_KIND_SWITCH(context.insts().Get(inst.witness_id)) { + case CARBON_KIND(SemIR::ImplWitness witness): { + // This is PerformAggregateAccess followed by GetConstantValueInSpecific. + auto witness_table = context.insts().GetAs( + witness.witness_table_id); + auto elements = context.inst_blocks().Get(witness_table.elements_id); + // `elements` can be empty if there is only a forward declaration of the + // impl. + if (!elements.empty()) { + auto index = static_cast(inst.index.index); + CARBON_CHECK(index < elements.size(), "Access out of bounds."); + auto element = elements[index]; + if (element.has_value()) { + LoadImportRef(context, element); + return ConstantEvalResult::Existing(GetConstantValueInSpecific( + context.sem_ir(), witness.specific_id, element)); + } + } + // If we get here, this impl witness table entry has not been populated + // yet, because the impl was referenced within its own definition. + // TODO: Add note pointing to the impl declaration. + context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet); + return ConstantEvalResult::Error; + } + case CARBON_KIND(SemIR::CustomWitness custom_witness): { + auto elements = context.inst_blocks().Get(custom_witness.elements_id); auto index = static_cast(inst.index.index); - CARBON_CHECK(index < elements.size(), "Access out of bounds."); - auto element = elements[index]; - if (element.has_value()) { - LoadImportRef(context, element); - return ConstantEvalResult::Existing(GetConstantValueInSpecific( - context.sem_ir(), witness->specific_id, element)); + // `elements` can be shorter than the number of associated entities while + // we're building the synthetic witness. + if (index < elements.size()) { + return ConstantEvalResult::Existing( + context.constant_values().Get(elements[index])); } + // If we get here, this synthesized witness table entry has not been + // populated yet. + // TODO: Is this reachable? We have no test coverage for this diagnostic. + context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet); + return ConstantEvalResult::Error; } - // If we get here, this impl witness table entry has not been populated yet, - // because the impl was referenced within its own definition. - // TODO: Add note pointing to the impl declaration. - context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet); - return ConstantEvalResult::Error; - } else if (auto cpp_witness = - context.insts().TryGetAs(inst.witness_id)) { - auto elements = context.inst_blocks().Get(cpp_witness->elements_id); - auto index = static_cast(inst.index.index); - // `elements` can be shorter than the number of associated entities while - // we're building the synthetic witness. - if (index < elements.size()) { - return ConstantEvalResult::Existing( - context.constant_values().Get(elements[index])); - } - // If we get here, this synthesized witness table entry has not been - // populated yet. - // TODO: Is this reachable? We have no test coverage for this diagnostic. - context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet); - return ConstantEvalResult::Error; - } else if (auto witness = context.insts().TryGetAs( - inst.witness_id)) { - // If the witness is symbolic but has a self type that is a FacetType, it - // can pull rewrite values from the self type. If the access is for one of - // those rewrites, evaluate to the RHS of the rewrite. + case CARBON_KIND(SemIR::LookupImplWitness witness): { + // If the witness is symbolic but has a self type that is a FacetType, it + // can pull rewrite values from the self type. If the access is for one of + // those rewrites, evaluate to the RHS of the rewrite. - auto witness_self_type_id = - context.insts().Get(witness->query_self_inst_id).type_id(); - if (!context.types().Is(witness_self_type_id)) { - return ConstantEvalResult::NewSamePhase(inst); - } - - // The `ImplWitnessAccess` is accessing a value, by index, for this - // interface. - auto access_interface_id = witness->query_specific_interface_id; - - auto witness_self_facet_type_id = - context.types() - .GetAs(witness_self_type_id) - .facet_type_id; - // TODO: We could consider something better than linear search here, such as - // a map. However that would probably require heap allocations which may be - // worse overall since the number of rewrite constraints is generally low. - // If the `rewrite_constraints` were sorted so that associated constants are - // grouped together, as in ResolveFacetTypeRewriteConstraints(), and limited - // to just the `ImplWitnessAccess` entries, then a binary search may work - // here. - for (auto witness_rewrite : context.facet_types() - .Get(witness_self_facet_type_id) - .rewrite_constraints) { - // Look at each rewrite constraint in the self facet value's type. If the - // LHS is an `ImplWitnessAccess` into the same interface that `inst` is - // indexing into, then we can use its RHS as the value. - auto witness_rewrite_lhs_access = - context.insts().TryGetAs( - witness_rewrite.lhs_id); - if (!witness_rewrite_lhs_access) { - continue; - } - if (witness_rewrite_lhs_access->index != inst.index) { - continue; + auto witness_self_type_id = + context.insts().Get(witness.query_self_inst_id).type_id(); + if (!context.types().Is(witness_self_type_id)) { + return ConstantEvalResult::NewSamePhase(inst); } - auto witness_rewrite_lhs_interface_id = - context.insts() - .GetAs( - witness_rewrite_lhs_access->witness_id) - .query_specific_interface_id; - if (witness_rewrite_lhs_interface_id != access_interface_id) { - continue; - } + // The `ImplWitnessAccess` is accessing a value, by index, for this + // interface. + auto access_interface_id = witness.query_specific_interface_id; - // The `ImplWitnessAccess` evaluates to the RHS from the witness self - // facet value's type. - return ConstantEvalResult::Existing( - context.constant_values().Get(witness_rewrite.rhs_id)); + auto witness_self_facet_type_id = + context.types() + .GetAs(witness_self_type_id) + .facet_type_id; + // TODO: We could consider something better than linear search here, such + // as a map. However that would probably require heap allocations which + // may be worse overall since the number of rewrite constraints is + // generally low. If the `rewrite_constraints` were sorted so that + // associated constants are grouped together, as in + // ResolveFacetTypeRewriteConstraints(), and limited to just the + // `ImplWitnessAccess` entries, then a binary search may work here. + for (auto witness_rewrite : context.facet_types() + .Get(witness_self_facet_type_id) + .rewrite_constraints) { + // Look at each rewrite constraint in the self facet value's type. If + // the LHS is an `ImplWitnessAccess` into the same interface that `inst` + // is indexing into, then we can use its RHS as the value. + auto witness_rewrite_lhs_access = + context.insts().TryGetAs( + witness_rewrite.lhs_id); + if (!witness_rewrite_lhs_access) { + continue; + } + if (witness_rewrite_lhs_access->index != inst.index) { + continue; + } + + auto witness_rewrite_lhs_interface_id = + context.insts() + .GetAs( + witness_rewrite_lhs_access->witness_id) + .query_specific_interface_id; + if (witness_rewrite_lhs_interface_id != access_interface_id) { + continue; + } + + // The `ImplWitnessAccess` evaluates to the RHS from the witness self + // facet value's type. + return ConstantEvalResult::Existing( + context.constant_values().Get(witness_rewrite.rhs_id)); + } + break; } + default: + break; } - return ConstantEvalResult::NewSamePhase(inst); } diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index ef74b7519793..da5dfbd544ec 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -594,4 +594,83 @@ auto GetOrAddImpl(Context& context, SemIR::LocId loc_id, return impl_id; } +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/impl.h b/toolchain/check/impl.h index 0c5cddc5399c..3b4518a1a295 100644 --- a/toolchain/check/impl.h +++ b/toolchain/check/impl.h @@ -58,6 +58,15 @@ auto GetOrAddImpl(Context& context, SemIR::LocId loc_id, SemIR::LocId implicit_params_loc_id, SemIR::Impl impl, Parse::NodeId extend_node) -> SemIR::ImplId; +// 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_IMPL_H_ diff --git a/toolchain/check/testdata/interop/cpp/impls/copy.carbon b/toolchain/check/testdata/interop/cpp/impls/copy.carbon index 74ff1b329731..acddcc69c93b 100644 --- a/toolchain/check/testdata/interop/cpp/impls/copy.carbon +++ b/toolchain/check/testdata/interop/cpp/impls/copy.carbon @@ -202,9 +202,9 @@ fn EqualWitnesses(p: Wrap(Cpp.Copyable)*) -> Wrap(Cpp.Copyable)* { // CHECK:STDOUT: %Copyable__carbon_thunk: %Copyable__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %Copyable.Op.type: type = fn_type @Copyable.Op [concrete] // CHECK:STDOUT: %Copyable.Op: %Copyable.Op.type = struct_value () [concrete] -// CHECK:STDOUT: %cpp_witness.524: = cpp_witness (%Copyable.Op) [concrete] -// CHECK:STDOUT: %Copy.facet.2cd: %Copy.type = facet_value %Copyable, (%cpp_witness.524) [concrete] -// CHECK:STDOUT: %.dc1: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.2cd [concrete] +// CHECK:STDOUT: %custom_witness.0b0: = custom_witness (%Copyable.Op) [concrete] +// CHECK:STDOUT: %Copy.facet.157: %Copy.type = facet_value %Copyable, (%custom_witness.0b0) [concrete] +// CHECK:STDOUT: %.07b: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.157 [concrete] // CHECK:STDOUT: %ExplicitCopy: type = class_type @ExplicitCopy [concrete] // CHECK:STDOUT: %ExplicitCopy.ExplicitCopy.type: type = fn_type @ExplicitCopy.ExplicitCopy [concrete] // CHECK:STDOUT: %ExplicitCopy.ExplicitCopy: %ExplicitCopy.ExplicitCopy.type = struct_value () [concrete] @@ -215,9 +215,9 @@ fn EqualWitnesses(p: Wrap(Cpp.Copyable)*) -> Wrap(Cpp.Copyable)* { // CHECK:STDOUT: %ExplicitCopy__carbon_thunk: %ExplicitCopy__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %ExplicitCopy.Op.type: type = fn_type @ExplicitCopy.Op [concrete] // CHECK:STDOUT: %ExplicitCopy.Op: %ExplicitCopy.Op.type = struct_value () [concrete] -// CHECK:STDOUT: %cpp_witness.b38: = cpp_witness (%ExplicitCopy.Op) [concrete] -// CHECK:STDOUT: %Copy.facet.27f: %Copy.type = facet_value %ExplicitCopy, (%cpp_witness.b38) [concrete] -// CHECK:STDOUT: %.82a: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.27f [concrete] +// CHECK:STDOUT: %custom_witness.fa3: = custom_witness (%ExplicitCopy.Op) [concrete] +// CHECK:STDOUT: %Copy.facet.40c: %Copy.type = facet_value %ExplicitCopy, (%custom_witness.fa3) [concrete] +// CHECK:STDOUT: %.167: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.40c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -247,7 +247,7 @@ fn EqualWitnesses(p: Wrap(Cpp.Copyable)*) -> Wrap(Cpp.Copyable)* { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %Copyable = name_ref c, %c // CHECK:STDOUT: -// CHECK:STDOUT: %impl.elem0: %.dc1 = impl_witness_access constants.%cpp_witness.524, element0 [concrete = constants.%Copyable.Op] +// CHECK:STDOUT: %impl.elem0: %.07b = impl_witness_access constants.%custom_witness.0b0, element0 [concrete = constants.%Copyable.Op] // CHECK:STDOUT: %bound_method: = bound_method %c.ref, %impl.elem0 // CHECK:STDOUT: %.loc8_10.1: ref %Copyable = temporary_storage // CHECK:STDOUT: %Op.ref: %Copyable.Copyable.type = name_ref Op, imports.%Copyable.Copyable.decl [concrete = constants.%Copyable.Copyable] @@ -266,7 +266,7 @@ fn EqualWitnesses(p: Wrap(Cpp.Copyable)*) -> Wrap(Cpp.Copyable)* { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %ExplicitCopy = name_ref c, %c // CHECK:STDOUT: -// CHECK:STDOUT: %impl.elem0: %.82a = impl_witness_access constants.%cpp_witness.b38, element0 [concrete = constants.%ExplicitCopy.Op] +// CHECK:STDOUT: %impl.elem0: %.167 = impl_witness_access constants.%custom_witness.fa3, element0 [concrete = constants.%ExplicitCopy.Op] // CHECK:STDOUT: %bound_method: = bound_method %c.ref, %impl.elem0 // CHECK:STDOUT: %.loc14_10.1: ref %ExplicitCopy = temporary_storage // CHECK:STDOUT: %Op.ref: %ExplicitCopy.ExplicitCopy.type = name_ref Op, imports.%ExplicitCopy.ExplicitCopy.decl [concrete = constants.%ExplicitCopy.ExplicitCopy] @@ -294,17 +294,17 @@ fn EqualWitnesses(p: Wrap(Cpp.Copyable)*) -> Wrap(Cpp.Copyable)* { // CHECK:STDOUT: %Copyable: type = class_type @Copyable [concrete] // CHECK:STDOUT: %Copyable.Op.type: type = fn_type @Copyable.Op [concrete] // CHECK:STDOUT: %Copyable.Op: %Copyable.Op.type = struct_value () [concrete] -// CHECK:STDOUT: %cpp_witness.524: = cpp_witness (%Copyable.Op) [concrete] -// CHECK:STDOUT: %Copy.facet.2cd: %Copy.type.705 = facet_value %Copyable, (%cpp_witness.524) [concrete] -// CHECK:STDOUT: %Copy.specific_fn: = specific_function %Copy, @Copy.loc6(%Copy.facet.2cd) [concrete] -// CHECK:STDOUT: %Wrap.380: type = class_type @Wrap, @Wrap(%Copy.facet.2cd) [concrete] -// CHECK:STDOUT: %ptr.ca9: type = ptr_type %Wrap.380 [concrete] -// CHECK:STDOUT: %Copy.impl_witness.5d2: = impl_witness imports.%Copy.impl_witness_table.027, @ptr.as.Copy.impl(%Wrap.380) [concrete] -// CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.a25: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Wrap.380) [concrete] -// CHECK:STDOUT: %ptr.as.Copy.impl.Op.a20: %ptr.as.Copy.impl.Op.type.a25 = struct_value () [concrete] -// CHECK:STDOUT: %Copy.facet.b54: %Copy.type.705 = facet_value %ptr.ca9, (%Copy.impl_witness.5d2) [concrete] -// CHECK:STDOUT: %.b2d: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.b54 [concrete] -// CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.a20, @ptr.as.Copy.impl.Op(%Wrap.380) [concrete] +// CHECK:STDOUT: %custom_witness.0b0: = custom_witness (%Copyable.Op) [concrete] +// CHECK:STDOUT: %Copy.facet.157: %Copy.type.705 = facet_value %Copyable, (%custom_witness.0b0) [concrete] +// CHECK:STDOUT: %Copy.specific_fn: = specific_function %Copy, @Copy.loc6(%Copy.facet.157) [concrete] +// CHECK:STDOUT: %Wrap.7dc: type = class_type @Wrap, @Wrap(%Copy.facet.157) [concrete] +// CHECK:STDOUT: %ptr.62c: type = ptr_type %Wrap.7dc [concrete] +// CHECK:STDOUT: %Copy.impl_witness.1e6: = impl_witness imports.%Copy.impl_witness_table.027, @ptr.as.Copy.impl(%Wrap.7dc) [concrete] +// CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.518: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Wrap.7dc) [concrete] +// CHECK:STDOUT: %ptr.as.Copy.impl.Op.fff: %ptr.as.Copy.impl.Op.type.518 = struct_value () [concrete] +// CHECK:STDOUT: %Copy.facet.622: %Copy.type.705 = facet_value %ptr.62c, (%Copy.impl_witness.1e6) [concrete] +// CHECK:STDOUT: %.19b: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.622 [concrete] +// CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.fff, @ptr.as.Copy.impl.Op(%Wrap.7dc) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -317,24 +317,24 @@ fn EqualWitnesses(p: Wrap(Cpp.Copyable)*) -> Wrap(Cpp.Copyable)* { // CHECK:STDOUT: %Copy.ref: %Copy.type.6f0 = name_ref Copy, file.%Copy.decl [concrete = constants.%Copy] // CHECK:STDOUT: %c.ref: %Copyable = name_ref c, %c // CHECK:STDOUT: -// CHECK:STDOUT: %Copy.facet.loc12_16.1: %Copy.type.705 = facet_value constants.%Copyable, (constants.%cpp_witness.524) [concrete = constants.%Copy.facet.2cd] -// CHECK:STDOUT: %.loc12_16.1: %Copy.type.705 = converted constants.%Copyable, %Copy.facet.loc12_16.1 [concrete = constants.%Copy.facet.2cd] -// CHECK:STDOUT: %Copy.facet.loc12_16.2: %Copy.type.705 = facet_value constants.%Copyable, (constants.%cpp_witness.524) [concrete = constants.%Copy.facet.2cd] -// CHECK:STDOUT: %.loc12_16.2: %Copy.type.705 = converted constants.%Copyable, %Copy.facet.loc12_16.2 [concrete = constants.%Copy.facet.2cd] -// CHECK:STDOUT: %Copy.specific_fn: = specific_function %Copy.ref, @Copy.loc6(constants.%Copy.facet.2cd) [concrete = constants.%Copy.specific_fn] +// CHECK:STDOUT: %Copy.facet.loc12_16.1: %Copy.type.705 = facet_value constants.%Copyable, (constants.%custom_witness.0b0) [concrete = constants.%Copy.facet.157] +// CHECK:STDOUT: %.loc12_16.1: %Copy.type.705 = converted constants.%Copyable, %Copy.facet.loc12_16.1 [concrete = constants.%Copy.facet.157] +// CHECK:STDOUT: %Copy.facet.loc12_16.2: %Copy.type.705 = facet_value constants.%Copyable, (constants.%custom_witness.0b0) [concrete = constants.%Copy.facet.157] +// CHECK:STDOUT: %.loc12_16.2: %Copy.type.705 = converted constants.%Copyable, %Copy.facet.loc12_16.2 [concrete = constants.%Copy.facet.157] +// CHECK:STDOUT: %Copy.specific_fn: = specific_function %Copy.ref, @Copy.loc6(constants.%Copy.facet.157) [concrete = constants.%Copy.specific_fn] // CHECK:STDOUT: // CHECK:STDOUT: %Copy.call: init %Copyable = call %Copy.specific_fn(%c.ref) to %.loc10_28 // CHECK:STDOUT: return %Copy.call to %return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @EqualWitnesses(%p.param: %ptr.ca9) -> %ptr.ca9 { +// CHECK:STDOUT: fn @EqualWitnesses(%p.param: %ptr.62c) -> %ptr.62c { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %p.ref: %ptr.ca9 = name_ref p, %p -// CHECK:STDOUT: %impl.elem0: %.b2d = impl_witness_access constants.%Copy.impl_witness.5d2, element0 [concrete = constants.%ptr.as.Copy.impl.Op.a20] +// CHECK:STDOUT: %p.ref: %ptr.62c = name_ref p, %p +// CHECK:STDOUT: %impl.elem0: %.19b = impl_witness_access constants.%Copy.impl_witness.1e6, element0 [concrete = constants.%ptr.as.Copy.impl.Op.fff] // CHECK:STDOUT: %bound_method.loc20_10.1: = bound_method %p.ref, %impl.elem0 -// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%Wrap.380) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%Wrap.7dc) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc20_10.2: = bound_method %p.ref, %specific_fn -// CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.ca9 = call %bound_method.loc20_10.2(%p.ref) +// CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.62c = call %bound_method.loc20_10.2(%p.ref) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index a6170526e407..70a51f3b57b3 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -56,8 +56,8 @@ CARBON_SEM_IR_INST_KIND(Converted) CARBON_SEM_IR_INST_KIND(CppOverloadSetType) CARBON_SEM_IR_INST_KIND(CppOverloadSetValue) CARBON_SEM_IR_INST_KIND(CppTemplateNameType) -CARBON_SEM_IR_INST_KIND(CppWitness) CARBON_SEM_IR_INST_KIND(CustomLayoutType) +CARBON_SEM_IR_INST_KIND(CustomWitness) CARBON_SEM_IR_INST_KIND(Deref) CARBON_SEM_IR_INST_KIND(ErrorInst) CARBON_SEM_IR_INST_KIND(ExportDecl) diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index 6544bb06ba30..1de4e72a9dd6 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -911,8 +911,8 @@ auto InstNamer::NamingContext::NameInst() -> void { AddInstNameId(sem_ir().entity_names().Get(inst.name_id).name_id, ".type"); return; } - case CppWitness::Kind: { - AddInstName("cpp_witness"); + case CustomWitness::Kind: { + AddInstName("custom_witness"); return; } case CARBON_KIND(FacetAccessType inst): { diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 7fc920e42cf0..250fa1af6a94 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -515,6 +515,46 @@ struct ConvertToValueAction { TypeInstId target_type_inst_id; }; +// The type of an overloaded C++ function. +struct CppOverloadSetType { + static constexpr auto Kind = + InstKind::CppOverloadSetType.Define( + {.ir_name = "cpp_overload_set_type", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::WheneverPossible}); + + TypeId type_id; + CppOverloadSetId overload_set_id; + SpecificId specific_id; +}; + +// An unresolved C++ overload set value. +struct CppOverloadSetValue { + static constexpr auto Kind = + InstKind::CppOverloadSetValue.Define( + {.ir_name = "cpp_overload_set_value", + .constant_kind = InstConstantKind::Always}); + + TypeId type_id; + CppOverloadSetId overload_set_id; +}; + +// The type of the name of a C++ template. The corresponding value is an empty +// `StructValue`. This does not handle function templates, which are instead +// represented as a `CppOverloadSetValue` of type `CppOverloadSetType`. +struct CppTemplateNameType { + // This is only ever created as a constant, so doesn't have a location. + static constexpr auto Kind = + InstKind::CppTemplateNameType.Define( + {.ir_name = "cpp_type_template_type", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::Always}); + + TypeId type_id; + EntityNameId name_id; + ClangDeclId decl_id; +}; + // A type whose layout is determined externally. This is used as the object // representation of class types imported from C++. struct CustomLayoutType { @@ -529,6 +569,22 @@ struct CustomLayoutType { CustomLayoutId layout_id; }; +// A witness synthesized for an arbitrary construct. For example, a `Destroy` +// witness, or a C++ overloaded operator. +struct CustomWitness { + static constexpr auto Kind = InstKind::CustomWitness.Define( + {.ir_name = "custom_witness", + .constant_kind = InstConstantKind::Always, + // TODO: For dynamic dispatch, we might want to lower witness tables as + // constants. + .is_lowered = false}); + + // Always the type of the builtin `WitnessType` singleton instruction. + TypeId type_id; + // The witness table of instructions. + InstBlockId elements_id; +}; + // The `*` dereference operator, as in `*pointer`. struct Deref { static constexpr auto Kind = InstKind::Deref.Define( @@ -730,62 +786,6 @@ struct FunctionTypeWithSelfType { InstId self_id; }; -// The type of an overloaded C++ function. -struct CppOverloadSetType { - static constexpr auto Kind = - InstKind::CppOverloadSetType.Define( - {.ir_name = "cpp_overload_set_type", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::WheneverPossible}); - - TypeId type_id; - CppOverloadSetId overload_set_id; - SpecificId specific_id; -}; - -// An unresolved C++ overload set value. -struct CppOverloadSetValue { - static constexpr auto Kind = - InstKind::CppOverloadSetValue.Define( - {.ir_name = "cpp_overload_set_value", - .constant_kind = InstConstantKind::Always}); - - TypeId type_id; - CppOverloadSetId overload_set_id; -}; - -// The type of the name of a C++ template. The corresponding value is an empty -// `StructValue`. This does not handle function templates, which are instead -// represented as a `CppOverloadSetValue` of type `CppOverloadSetType`. -struct CppTemplateNameType { - // This is only ever created as a constant, so doesn't have a location. - static constexpr auto Kind = - InstKind::CppTemplateNameType.Define( - {.ir_name = "cpp_type_template_type", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - - TypeId type_id; - EntityNameId name_id; - ClangDeclId decl_id; -}; - -// A witness synthesized for a C++ construct such as a constructor, conversion -// function, or overloaded operator. -struct CppWitness { - static constexpr auto Kind = InstKind::CppWitness.Define( - {.ir_name = "cpp_witness", - .constant_kind = InstConstantKind::Always, - // TODO: For dynamic dispatch, we might want to lower witness tables as - // constants. - .is_lowered = false}); - - // Always the type of the builtin `WitnessType` singleton instruction. - TypeId type_id; - // The witness table of instructions. - InstBlockId elements_id; -}; - // The type of the name of a generic class. The corresponding value is an empty // `StructValue`. struct GenericClassType { @@ -2094,7 +2094,7 @@ struct WhereExpr { InstBlockId requirements_id; }; -// The type of `ImplWitness`, `CppWitness`, and `LookupImplWitness` +// The type of `ImplWitness`, `CustomWitness`, and `LookupImplWitness` // instructions. The latter will evaluate at some point during specific // computation into one of the former two, and their types should not change in // the process.