teaches arithmetic interfaces about C++ operators (#7123)

This commit is contained in:
Christopher Di Bella
2026-04-28 01:00:21 +00:00
committed by GitHub
parent aa8f9d8c00
commit 9480c10ecf
11 changed files with 4160 additions and 45 deletions
+98
View File
@@ -6,8 +6,10 @@
#include "clang/Sema/Sema.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/core_identifier.h"
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/cpp/location.h"
#include "toolchain/check/cpp/operators.h"
#include "toolchain/check/cpp/overload_resolution.h"
#include "toolchain/check/custom_witness.h"
#include "toolchain/check/impl.h"
@@ -231,6 +233,74 @@ static auto BuildDestroyWitness(
query_specific_interface_id, {fn_id});
}
// Attempts to build a witness table entry for a C++ unary operator.
static auto BuildCppUnaryOperatorWitness(
Context& context, SemIR::LocId loc_id, SemIR::CoreInterface core_interface,
bool has_associated_result_type, SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId {
auto self_type_id =
context.types().GetTypeIdForTypeConstantId(query_self_const_id);
auto fn_id = LookupCppOperator(
context, loc_id, {.interface_name = AsCoreIdentifier(core_interface)},
{self_type_id});
if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) {
return fn_id;
}
if (has_associated_result_type) {
auto result_type_id =
context.functions()
.Get(context.insts().GetAs<SemIR::FunctionDecl>(fn_id).function_id)
.return_type_inst_id;
if (result_type_id == SemIR::ErrorInst::InstId) {
return SemIR::ErrorInst::InstId;
}
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface_id,
{result_type_id, fn_id});
}
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface_id, {fn_id});
}
// Attempts to build a witness table entry for a C++ binary operator.
static auto BuildCppBinaryOperatorWitness(
Context& context, SemIR::LocId loc_id, SemIR::CoreInterface core_interface,
bool has_associated_result_type, SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId {
auto self_type_id =
context.types().GetTypeIdForTypeConstantId(query_self_const_id);
auto args =
context.inst_blocks().Get(context.specifics()
.Get(context.specific_interfaces()
.Get(query_specific_interface_id)
.specific_id)
.args_id);
CARBON_CHECK(args.size() == 1, "Binary operator missing an argument");
auto arg_type_id = context.types().GetTypeIdForTypeInstId(args.front());
auto fn_id = LookupCppOperator(
context, loc_id, {.interface_name = AsCoreIdentifier(core_interface)},
{self_type_id, arg_type_id});
if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) {
return fn_id;
}
if (has_associated_result_type) {
auto result_type_id =
context.functions()
.Get(context.insts().GetAs<SemIR::FunctionDecl>(fn_id).function_id)
.return_type_inst_id;
if (result_type_id == SemIR::ErrorInst::InstId) {
return SemIR::ErrorInst::InstId;
}
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface_id,
{result_type_id, fn_id});
}
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface_id, {fn_id});
}
auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
SemIR::CoreInterface core_interface,
SemIR::ConstantId query_self_const_id,
@@ -243,6 +313,34 @@ auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
static_cast<void>(best_impl_loc_id);
switch (core_interface) {
case SemIR::CoreInterface::Inc:
case SemIR::CoreInterface::Dec:
return BuildCppUnaryOperatorWitness(context, loc_id, core_interface,
/*has_associated_result_type=*/false,
query_self_const_id,
query_specific_interface_id);
case SemIR::CoreInterface::Negate:
return BuildCppUnaryOperatorWitness(
context, loc_id, core_interface, /*has_associated_result_type=*/true,
query_self_const_id, query_specific_interface_id);
case SemIR::CoreInterface::AddWith:
case SemIR::CoreInterface::SubWith:
case SemIR::CoreInterface::MulWith:
case SemIR::CoreInterface::DivWith:
case SemIR::CoreInterface::ModWith:
return BuildCppBinaryOperatorWitness(context, loc_id, core_interface,
/*has_associated_result_type=*/true,
query_self_const_id,
query_specific_interface_id);
case SemIR::CoreInterface::AddAssignWith:
case SemIR::CoreInterface::SubAssignWith:
case SemIR::CoreInterface::MulAssignWith:
case SemIR::CoreInterface::DivAssignWith:
case SemIR::CoreInterface::ModAssignWith:
return BuildCppBinaryOperatorWitness(context, loc_id, core_interface,
/*has_associated_result_type=*/false,
query_self_const_id,
query_specific_interface_id);
case SemIR::CoreInterface::Copy:
return BuildCopyWitness(context, loc_id, query_self_const_id,
query_specific_interface_id);
+109 -14
View File
@@ -453,6 +453,90 @@ static auto LookupCppConversion(Context& context, SemIR::LocId loc_id,
return SemIR::InstId::None;
}
static auto FindClangOperator(Context& context, SemIR::LocId loc_id,
clang::OverloadedOperatorKind op_kind,
llvm::ArrayRef<clang::Expr*> arg_exprs)
-> SemIR::InstId;
namespace {
struct DiagnoseIncompleteOperandTypeInCppOperatorLookup {
Context& context;
SemIR::TypeId arg_type_id;
SemIR::LocId loc_id;
void operator()(auto& builder) const {
CARBON_DIAGNOSTIC(
IncompleteOperandTypeInCppOperatorLookup, Context,
"looking up a C++ operator with incomplete operand type {0}",
SemIR::TypeId);
builder.Context(loc_id, IncompleteOperandTypeInCppOperatorLookup,
arg_type_id);
}
};
} // namespace
auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
llvm::ArrayRef<SemIR::TypeId> arg_type_ids)
-> SemIR::InstId {
// Register an annotation scope to flush any Clang diagnostics when we return.
// This is important to ensure that Clang diagnostics are properly interleaved
// with Carbon diagnostics.
Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
[](auto& /*builder*/) {});
if (op.interface_name == CoreIdentifier::ImplicitAs ||
op.interface_name == CoreIdentifier::As) {
context.TODO(loc_id, "handle `as` operator when passed a type");
return SemIR::ErrorInst::InstId;
}
auto op_kind =
GetClangOperatorKind(context, loc_id, op.interface_name, op.op_name);
if (!op_kind) {
return SemIR::ErrorInst::InstId;
}
for (SemIR::TypeId arg_type_id : arg_type_ids) {
if (!RequireCompleteType(context, arg_type_id, loc_id,
DiagnoseIncompleteOperandTypeInCppOperatorLookup{
.context = context,
.arg_type_id = arg_type_id,
.loc_id = loc_id})) {
return SemIR::ErrorInst::InstId;
}
}
struct Operand {
using enum clang::ExprValueKind;
explicit Operand(clang::QualType type)
: type(type),
expression({}, type,
type->isLValueReferenceType() ? VK_LValue
: type->isRValueReferenceType() ? VK_XValue
: VK_PRValue) {}
clang::QualType type;
clang::OpaqueValueExpr expression;
};
auto cpp_type = MapToCppType(context, arg_type_ids[0]);
if (cpp_type.isNull()) {
return SemIR::InstId::None;
}
auto arg0 = Operand(cpp_type);
if (arg_type_ids.size() == 1) {
return FindClangOperator(context, loc_id, *op_kind, {&arg0.expression});
}
CARBON_CHECK(arg_type_ids.size() == 2);
cpp_type = MapToCppType(context, arg_type_ids[1]);
if (cpp_type.isNull()) {
return SemIR::InstId::None;
}
auto arg1 = Operand(cpp_type);
return FindClangOperator(context, loc_id, *op_kind,
{&arg0.expression, &arg1.expression});
}
auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
// Register an annotation scope to flush any Clang diagnostics when we return.
@@ -461,6 +545,14 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
[](auto& /*builder*/) {});
// We can only handle concrete types in LookupCppOperator.
for (auto arg_id : arg_ids) {
auto type_id = context.insts().Get(arg_id).type_id();
if (type_id.is_symbolic()) {
return SemIR::InstId::None;
}
}
// Handle `ImplicitAs` and `As`.
if (op.interface_name == CoreIdentifier::ImplicitAs ||
op.interface_name == CoreIdentifier::As) {
@@ -490,14 +582,11 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
// Make sure all operands are complete before lookup.
for (SemIR::InstId arg_id : arg_ids) {
SemIR::TypeId arg_type_id = context.insts().Get(arg_id).type_id();
if (!RequireCompleteType(context, arg_type_id, loc_id, [&](auto& builder) {
CARBON_DIAGNOSTIC(
IncompleteOperandTypeInCppOperatorLookup, Context,
"looking up a C++ operator with incomplete operand type {0}",
SemIR::TypeId);
builder.Context(loc_id, IncompleteOperandTypeInCppOperatorLookup,
arg_type_id);
})) {
if (!RequireCompleteType(context, arg_type_id, loc_id,
DiagnoseIncompleteOperandTypeInCppOperatorLookup{
.context = context,
.arg_type_id = arg_type_id,
.loc_id = loc_id})) {
return SemIR::ErrorInst::InstId;
}
}
@@ -506,18 +595,24 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
if (!maybe_arg_exprs.has_value()) {
return SemIR::ErrorInst::InstId;
}
auto& arg_exprs = *maybe_arg_exprs;
return FindClangOperator(context, loc_id, *op_kind, *maybe_arg_exprs);
}
static auto FindClangOperator(Context& context, SemIR::LocId loc_id,
clang::OverloadedOperatorKind op_kind,
llvm::ArrayRef<clang::Expr*> arg_exprs)
-> SemIR::InstId {
clang::SourceLocation loc = GetCppLocation(context, loc_id);
clang::OverloadCandidateSet::OperatorRewriteInfo operator_rewrite_info(
*op_kind, loc, /*AllowRewritten=*/true);
op_kind, loc, /*AllowRewritten=*/true);
clang::OverloadCandidateSet candidate_set(
loc, clang::OverloadCandidateSet::CSK_Operator, operator_rewrite_info);
clang::Sema& sema = context.clang_sema();
// This works for both unary and binary operators.
sema.LookupOverloadedBinOp(candidate_set, *op_kind, clang::UnresolvedSet<0>{},
sema.LookupOverloadedBinOp(candidate_set, op_kind, clang::UnresolvedSet<0>{},
arg_exprs);
clang::OverloadCandidateSet::iterator best_viable_fn;
@@ -540,7 +635,7 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
// If this is an operator method, the first arg will be used as self.
int32_t num_params = arg_ids.size();
int32_t num_params = arg_exprs.size();
if (isa<clang::CXXMethodDecl>(best_viable_fn->Function)) {
--num_params;
}
@@ -561,7 +656,7 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
return SemIR::InstId::None;
}
case clang::OverloadingResult::OR_Ambiguous: {
const char* spelling = clang::getOperatorSpelling(*op_kind);
const char* spelling = clang::getOperatorSpelling(op_kind);
candidate_set.NoteCandidates(
clang::PartialDiagnosticAt(
loc, sema.PDiag(clang::diag::err_ovl_ambiguous_oper_binary)
@@ -571,7 +666,7 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
return SemIR::ErrorInst::InstId;
}
case clang::OverloadingResult::OR_Deleted:
const char* spelling = clang::getOperatorSpelling(*op_kind);
const char* spelling = clang::getOperatorSpelling(op_kind);
auto* message = best_viable_fn->Function->getDeletedMessage();
// The best viable function might be a different operator if the best
// candidate is a rewritten candidate, so use the operator kind of the
+10
View File
@@ -17,6 +17,16 @@ namespace Carbon::Check {
auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId;
// Looks up the given operator in the Clang AST generated when importing C++
// code using argument dependent lookup (ADL) and return overload set
// instruction.
//
// This overload synthesises objects in an unevaluated context within the Clang
// AST based on the types it is provided.
auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
llvm::ArrayRef<SemIR::TypeId> arg_type_ids)
-> SemIR::InstId;
// Returns whether the decl is an operator member function.
auto IsCppOperatorMethodDecl(clang::Decl* decl) -> bool;
+58
View File
@@ -527,6 +527,51 @@ auto BuildCustomWitness(Context& context, SemIR::LocId loc_id,
context.inst_blocks().Add(entries));
}
auto AsCoreIdentifier(SemIR::CoreInterface core_interface) -> CoreIdentifier {
using SemIR::CoreInterface;
switch (core_interface) {
case CoreInterface::AddAssignWith:
return CoreIdentifier::AddAssignWith;
case CoreInterface::AddWith:
return CoreIdentifier::AddWith;
case CoreInterface::Copy:
return CoreIdentifier::Copy;
case CoreInterface::CppUnsafeDeref:
return CoreIdentifier::CppUnsafeDeref;
case CoreInterface::Dec:
return CoreIdentifier::Dec;
case CoreInterface::Default:
return CoreIdentifier::Default;
case CoreInterface::Destroy:
return CoreIdentifier::Destroy;
case CoreInterface::DivAssignWith:
return CoreIdentifier::DivAssignWith;
case CoreInterface::DivWith:
return CoreIdentifier::DivWith;
case CoreInterface::Inc:
return CoreIdentifier::Inc;
case CoreInterface::IntFitsIn:
return CoreIdentifier::IntFitsIn;
case CoreInterface::ModAssignWith:
return CoreIdentifier::ModAssignWith;
case CoreInterface::ModWith:
return CoreIdentifier::ModWith;
case CoreInterface::MulAssignWith:
return CoreIdentifier::MulAssignWith;
case CoreInterface::MulWith:
return CoreIdentifier::MulWith;
case CoreInterface::Negate:
return CoreIdentifier::Negate;
case CoreInterface::SubAssignWith:
return CoreIdentifier::SubAssignWith;
case CoreInterface::SubWith:
return CoreIdentifier::SubWith;
case CoreInterface::Unknown:
CARBON_FATAL("{0} doesn't have a `CoreIdentifier` mapping",
core_interface);
}
}
auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id)
-> SemIR::CoreInterface {
const auto& interface = context.interfaces().Get(interface_id);
@@ -665,9 +710,22 @@ auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
case SemIR::CoreInterface::IntFitsIn:
return MakeIntFitsInWitness(context, loc_id, query_self_const_id,
query_specific_interface_id, build_witness);
case SemIR::CoreInterface::AddAssignWith:
case SemIR::CoreInterface::AddWith:
case SemIR::CoreInterface::Copy:
case SemIR::CoreInterface::CppUnsafeDeref:
case SemIR::CoreInterface::Dec:
case SemIR::CoreInterface::Default:
case SemIR::CoreInterface::DivAssignWith:
case SemIR::CoreInterface::DivWith:
case SemIR::CoreInterface::Inc:
case SemIR::CoreInterface::ModAssignWith:
case SemIR::CoreInterface::ModWith:
case SemIR::CoreInterface::MulAssignWith:
case SemIR::CoreInterface::MulWith:
case SemIR::CoreInterface::Negate:
case SemIR::CoreInterface::SubAssignWith:
case SemIR::CoreInterface::SubWith:
case SemIR::CoreInterface::Unknown:
// TODO: Handle more interfaces, particularly copy, move, and conversion.
return std::nullopt;
+3
View File
@@ -30,6 +30,9 @@ auto BuildPrimitiveCopyWitness(
auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id)
-> SemIR::CoreInterface;
// Maps a `CoreInterface` to its `CoreIdentifier` equivalent.
auto AsCoreIdentifier(SemIR::CoreInterface core_interface) -> CoreIdentifier;
// Returns a witness for a `CoreInterface` `CustomWitness`. A return value of
// `None` indicates a non-final witness should be produced, while `std::nullopt`
// indicates the query is final and no witness can be produced.
+4
View File
@@ -250,6 +250,10 @@ auto CheckFunctionReturnTypeMatches(Context& context,
}
if (!context.types().AreEqualAcrossDeclarations(new_return_type_id,
prev_return_type_id)) {
if (new_function.name_id == SemIR::NameId::CppOperator &&
!prev_return_type_id.has_value()) {
return true;
}
if (!diagnose) {
return false;
}
+14
View File
@@ -16,6 +16,7 @@
#include "toolchain/check/name_component.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/type.h"
#include "toolchain/sem_ir/core_interface.h"
#include "toolchain/sem_ir/entity_with_params_base.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/interface.h"
@@ -92,11 +93,24 @@ static auto BuildInterfaceDecl(Context& context,
auto name = context.names().GetIRBaseName(interface_info.name_id);
interface_info.core_interface =
llvm::StringSwitch<SemIR::CoreInterface>(name)
.Case("AddAssignWith", SemIR::CoreInterface::AddAssignWith)
.Case("AddWith", SemIR::CoreInterface::AddWith)
.Case("Copy", SemIR::CoreInterface::Copy)
.Case("CppUnsafeDeref", SemIR::CoreInterface::CppUnsafeDeref)
.Case("Dec", SemIR::CoreInterface::Dec)
.Case("Default", SemIR::CoreInterface::Default)
.Case("Destroy", SemIR::CoreInterface::Destroy)
.Case("DivAssignWith", SemIR::CoreInterface::DivAssignWith)
.Case("DivWith", SemIR::CoreInterface::DivWith)
.Case("Inc", SemIR::CoreInterface::Inc)
.Case("IntFitsIn", SemIR::CoreInterface::IntFitsIn)
.Case("ModAssignWith", SemIR::CoreInterface::ModAssignWith)
.Case("ModWith", SemIR::CoreInterface::ModWith)
.Case("MulAssignWith", SemIR::CoreInterface::MulAssignWith)
.Case("MulWith", SemIR::CoreInterface::MulWith)
.Case("Negate", SemIR::CoreInterface::Negate)
.Case("SubAssignWith", SemIR::CoreInterface::SubAssignWith)
.Case("SubWith", SemIR::CoreInterface::SubWith)
.Default(SemIR::CoreInterface::Unknown);
}
interface_decl.interface_id = context.interfaces().Add(interface_info);
File diff suppressed because it is too large Load Diff
@@ -822,35 +822,6 @@ fn F() {
//@dump-sem-ir-end
}
// ============================================================================
// Satisfying constraints
// ============================================================================
// --- fail_todo_constraints.carbon
library "[[@TEST_NAME]]";
import Cpp inline '''
class X {};
X operator+(X, X);
''';
// TODO: Use Core.Add() when it is available.
// TODO: `Sum()` should be declared this way when generics implementation is ready for that:
// fn Sum[T:! Core.AddWith(.Self) where .Result = .Self](a: T, b: T) -> T
fn Sum[U:! type, T:! Core.AddWith(U) where .Result = U](a: T, b: U) -> U {
return a + b;
}
fn Call(x: Cpp.X) -> Cpp.X {
// CHECK:STDERR: fail_todo_constraints.carbon:[[@LINE+7]]:10: error: cannot convert type `Cpp.X` into type implementing `Core.AddWith(Cpp.X) where .(Core.AddWith(Cpp.X).Result) = Cpp.X` [ConversionFailureTypeToFacet]
// CHECK:STDERR: return Sum(x, x);
// CHECK:STDERR: ^~~~~~~~~
// CHECK:STDERR: fail_todo_constraints.carbon:[[@LINE-7]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
// CHECK:STDERR: fn Sum[U:! type, T:! Core.AddWith(U) where .Result = U](a: T, b: U) -> U {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
return Sum(x, x);
}
// ============================================================================
// Operator not found
+6 -2
View File
@@ -472,7 +472,9 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_id,
// TODO: For virtual functions, we want different rules for checking `self`.
// TODO: This is too strict; for example, we should not compare parameter
// names here.
if (CheckFunctionTypeMatches(
if (context.functions().Get(callee.function_id).special_function_kind !=
SemIR::Function::SpecialFunctionKind::HasCppThunk &&
CheckFunctionTypeMatches(
context, context.functions().Get(callee.function_id),
context.functions().Get(signature_id), signature_specific_id,
/*check_syntax=*/false, /*check_self=*/true, /*diagnose=*/false)) {
@@ -489,7 +491,9 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_id,
// thunk, and always convert the result of the wrapped call to the return type
// of the thunk.
if (!HasDeclaredReturnType(context, signature_id) &&
HasDeclaredReturnType(context, callee.function_id)) {
HasDeclaredReturnType(context, callee.function_id) &&
context.functions().Get(callee.function_id).name_id !=
SemIR::NameId::CppOperator) {
bool success = CheckFunctionReturnTypeMatches(
context, context.functions().Get(callee.function_id),
context.functions().Get(signature_id), signature_specific_id);
+13
View File
@@ -16,11 +16,24 @@
#define CARBON_SEM_IR_CORE_INTERFACE_KIND(Name)
#endif
CARBON_SEM_IR_CORE_INTERFACE_KIND(AddAssignWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(AddWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(Copy)
CARBON_SEM_IR_CORE_INTERFACE_KIND(CppUnsafeDeref)
CARBON_SEM_IR_CORE_INTERFACE_KIND(Dec)
CARBON_SEM_IR_CORE_INTERFACE_KIND(Default)
CARBON_SEM_IR_CORE_INTERFACE_KIND(Destroy)
CARBON_SEM_IR_CORE_INTERFACE_KIND(DivAssignWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(DivWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(Inc)
CARBON_SEM_IR_CORE_INTERFACE_KIND(IntFitsIn)
CARBON_SEM_IR_CORE_INTERFACE_KIND(ModAssignWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(ModWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(MulAssignWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(MulWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(Negate)
CARBON_SEM_IR_CORE_INTERFACE_KIND(SubAssignWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(SubWith)
CARBON_SEM_IR_CORE_INTERFACE_KIND(Unknown)