Make C++ enum types impl Core.Copy. (#7013)

Remove special-case handling in conversion logic for C++ enum types,
synthesize a custom witness of `Core.Copy` using the `primitive_copy`
builtin function.
This commit is contained in:
Richard Smith
2026-04-02 19:23:42 +00:00
committed by GitHub
parent 0851d657c8
commit 6f0ec37a8b
6 changed files with 107 additions and 77 deletions
+43 -22
View File
@@ -15,26 +15,34 @@
#include "toolchain/check/import_ref.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/type.h"
#include "toolchain/sem_ir/builtin_function_kind.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
// If the given type is a C++ class type, returns the corresponding class
// declaration. Otherwise returns nullptr.
// TODO: Handle qualified types.
static auto TypeAsClassDecl(Context& context,
SemIR::ConstantId query_self_const_id)
-> clang::CXXRecordDecl* {
// Given a type constant, return the corresponding class scope if there is one.
static auto GetClassScope(Context& context,
SemIR::ConstantId query_self_const_id)
-> SemIR::NameScopeId {
auto class_type = context.constant_values().TryGetInstAs<SemIR::ClassType>(
query_self_const_id);
if (!class_type) {
// Not a class.
return nullptr;
return SemIR::NameScopeId::None;
}
return context.classes().Get(class_type->class_id).scope_id;
}
// If the given type is a C++ tag (class or enumeration) type, returns the
// corresponding tag declaration. Otherwise returns nullptr.
// TODO: Handle qualified types.
static auto TypeAsTagDecl(Context& context,
SemIR::ConstantId query_self_const_id)
-> clang::TagDecl* {
SemIR::NameScopeId class_scope_id =
context.classes().Get(class_type->class_id).scope_id;
GetClassScope(context, query_self_const_id);
if (!class_scope_id.has_value()) {
return nullptr;
}
@@ -45,8 +53,16 @@ static auto TypeAsClassDecl(Context& context,
return nullptr;
}
return dyn_cast<clang::CXXRecordDecl>(
context.clang_decls().Get(decl_id).key.decl);
return dyn_cast<clang::TagDecl>(context.clang_decls().Get(decl_id).key.decl);
}
// If the given type is a C++ class type, returns the corresponding class
// declaration. Otherwise returns nullptr.
static auto TypeAsClassDecl(Context& context,
SemIR::ConstantId query_self_const_id)
-> clang::CXXRecordDecl* {
return dyn_cast_or_null<clang::CXXRecordDecl>(
TypeAsTagDecl(context, query_self_const_id));
}
namespace {
@@ -92,21 +108,26 @@ static auto BuildCopyWitness(
SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId {
auto& clang_sema = context.clang_sema();
// TODO: This should provide `Copy` for enums and other trivially copyable
// types.
auto* class_decl = TypeAsClassDecl(context, query_self_const_id);
if (!class_decl) {
auto* tag_decl = TypeAsTagDecl(context, query_self_const_id);
if (!tag_decl) {
return SemIR::InstId::None;
}
auto decl_info = DeclInfo{.decl = clang_sema.LookupCopyingConstructor(
class_decl, clang::Qualifiers::Const),
.signature = {.num_params = 1}};
auto fn_id = GetFunctionId(context, loc_id, decl_info);
if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) {
return fn_id;
if (auto* class_decl = dyn_cast<clang::CXXRecordDecl>(tag_decl)) {
auto decl_info = DeclInfo{.decl = clang_sema.LookupCopyingConstructor(
class_decl, clang::Qualifiers::Const),
.signature = {.num_params = 1}};
auto fn_id = GetFunctionId(context, loc_id, decl_info);
if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) {
return fn_id;
}
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface_id, {fn_id});
}
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface_id, {fn_id});
// Otherwise it's an enum (or eventually a C struct type). Perform a primitive
// copy.
return BuildPrimitiveCopyWitness(
context, loc_id, GetClassScope(context, query_self_const_id),
query_self_const_id, query_specific_interface_id);
}
static auto BuildCppUnsafeDerefWitness(