mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:40:10 +01:00
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:
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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): {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user