Refactor CppWitness as CustomWitness (#6491)

This is in anticipation of using the same construct for all
implementations of `Destroy`, as well as other similar use-cases with
language-defined interfaces.
This commit is contained in:
Jon Ross-Perkins
2025-12-15 19:41:14 +00:00
committed by GitHub
parent 6b28213b36
commit 25f63140e6
8 changed files with 271 additions and 261 deletions
+2 -84
View File
@@ -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<SemIR::InstId> 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<SemIR::InstId> 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<SemIR::CppWitness>(
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<SemIR::FunctionType>(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,
+92 -88
View File
@@ -6,6 +6,7 @@
#include <variant>
#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<SemIR::ImplWitness>(inst.witness_id)) {
// This is PerformAggregateAccess followed by GetConstantValueInSpecific.
auto witness_table = context.insts().GetAs<SemIR::ImplWitnessTable>(
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<SemIR::ImplWitnessTable>(
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<size_t>(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<size_t>(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<SemIR::CppWitness>(inst.witness_id)) {
auto elements = context.inst_blocks().Get(cpp_witness->elements_id);
auto index = static_cast<size_t>(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<SemIR::LookupImplWitness>(
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<SemIR::FacetType>(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<SemIR::FacetType>(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<SemIR::ImplWitnessAccess>(
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<SemIR::FacetType>(witness_self_type_id)) {
return ConstantEvalResult::NewSamePhase(inst);
}
auto witness_rewrite_lhs_interface_id =
context.insts()
.GetAs<SemIR::LookupImplWitness>(
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<SemIR::FacetType>(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<SemIR::ImplWitnessAccess>(
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<SemIR::LookupImplWitness>(
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);
}
+79
View File
@@ -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<SemIR::InstId> 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<SemIR::InstId> 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<SemIR::CustomWitness>(
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<SemIR::FunctionType>(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
+9
View File
@@ -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<SemIR::InstId> values) -> SemIR::InstId;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_IMPL_H_
+29 -29
View File
@@ -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: <witness> = 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: <witness> = 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: <witness> = 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: <witness> = 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: <elided>
// 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> = 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: <elided>
// 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> = 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: <witness> = 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> = 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: <witness> = 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> = specific_function %ptr.as.Copy.impl.Op.a20, @ptr.as.Copy.impl.Op(%Wrap.380) [concrete]
// CHECK:STDOUT: %custom_witness.0b0: <witness> = 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> = 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: <witness> = 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> = 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: <elided>
// 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> = 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> = specific_function %Copy.ref, @Copy.loc6(constants.%Copy.facet.157) [concrete = constants.%Copy.specific_fn]
// CHECK:STDOUT: <elided>
// 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> = bound_method %p.ref, %impl.elem0
// CHECK:STDOUT: %specific_fn: <specific function> = 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> = 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> = 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:
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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): {
+57 -57
View File
@@ -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<Parse::NodeId>(
{.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<Parse::NodeId>(
{.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<Parse::NoneNodeId>(
{.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<Parse::NodeId>(
{.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<Parse::NodeId>(
@@ -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<Parse::NodeId>(
{.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<Parse::NodeId>(
{.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<Parse::NoneNodeId>(
{.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<Parse::NodeId>(
{.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.