From 0745bda89969ef833087587f4212a9f660ad3165 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Mon, 24 Aug 2026 18:53:24 +0000 Subject: [PATCH] Support using Carbon generic types as C++ template parameters (#7673) Example: ```carbon import Cpp library ""; class C(T: type) { var v: Cpp.std.vector(T); } inline Cpp ''' void F() { Carbon::C c; c.v.push_back(123); std::cout << c.v.back() << std::endl; } '''; ``` A new `CallCppTemplateAction` is used to delay performing the C++ template call until non-symbolic arguments are known. --- toolchain/check/action.cpp | 26 ++- toolchain/check/action.h | 4 + toolchain/check/cpp/call.cpp | 15 +- .../interop/cpp/class/import/template.carbon | 181 ++++++++++++++++++ .../interop/cpp/template/generic_call.carbon | 8 +- toolchain/sem_ir/formatter.cpp | 2 + toolchain/sem_ir/formatter.h | 1 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/type_iterator.cpp | 1 + toolchain/sem_ir/typed_insts.h | 16 ++ 10 files changed, 248 insertions(+), 7 deletions(-) diff --git a/toolchain/check/action.cpp b/toolchain/check/action.cpp index dc865fe27fd7..fba914c2fc35 100644 --- a/toolchain/check/action.cpp +++ b/toolchain/check/action.cpp @@ -71,7 +71,7 @@ template requires SemIR::Internal::IsIdKindType && SameAsOneOf + SemIR::ElementIndex, SemIR::ClangDeclId> static auto OperandDependence(Context& /*context*/, IdT /*id*/) -> SemIR::ConstantDependence { return SemIR::ConstantDependence::None; @@ -88,16 +88,22 @@ static auto OperandDependence(Context& context, context.bundles().GetAsTuple(bundle_id)); } -static auto OperandDependence(Context& context, SemIR::SpecificId specific_id) +static auto OperandDependence(Context& context, + SemIR::InstBlockId inst_block_id) -> SemIR::ConstantDependence { - auto specific = context.specifics().Get(specific_id); auto result = SemIR::ConstantDependence::None; - for (auto arg_id : context.inst_blocks().Get(specific.args_id)) { + for (auto arg_id : context.inst_blocks().Get(inst_block_id)) { result = std::max(result, OperandDependence(context, arg_id)); } return result; } +static auto OperandDependence(Context& context, SemIR::SpecificId specific_id) + -> SemIR::ConstantDependence { + auto specific = context.specifics().Get(specific_id); + return OperandDependence(context, specific.args_id); +} + template requires SemIR::Internal::IsIdKindType static auto OperandDependence(Context& /*context*/, IdT /*id*/) @@ -113,6 +119,18 @@ static auto OperandDependence(Context& context, SemIR::IdAndKind arg) } auto ActionIsPerformable(Context& context, SemIR::Inst action_inst) -> bool { + if (auto action = action_inst.TryAs()) { + auto args = context.inst_blocks().Get(action->args_id); + for (auto arg : args) { + auto const_id = context.constant_values().Get(arg); + if (const_id.is_symbolic()) { + return false; + } + } + + return true; + } + if (auto refine_action = action_inst.TryAs()) { // `RefineTypeAction` can be performed whenever the type is not template- // dependent, even if we don't know the instruction yet. diff --git a/toolchain/check/action.h b/toolchain/check/action.h index e51b5e91b196..3e40405933e0 100644 --- a/toolchain/check/action.h +++ b/toolchain/check/action.h @@ -19,6 +19,10 @@ auto PerformAction(Context& context, SemIR::LocId loc_id, auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::AccessOptionalMemberAction action) -> SemIR::InstId; +// Performs a C++ template call action. Defined in cpp/call.cpp. +auto PerformAction(Context& context, SemIR::LocId loc_id, + SemIR::CallCppTemplateAction action) -> SemIR::InstId; + // Performs a conversion action. Defined in convert.cpp. auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::ConvertAction action) -> SemIR::InstId; diff --git a/toolchain/check/cpp/call.cpp b/toolchain/check/cpp/call.cpp index 525ddaf570c7..433c32935f28 100644 --- a/toolchain/check/cpp/call.cpp +++ b/toolchain/check/cpp/call.cpp @@ -7,6 +7,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/Template.h" #include "toolchain/base/kind_switch.h" +#include "toolchain/check/action.h" #include "toolchain/check/call.h" #include "toolchain/check/cpp/constant.h" #include "toolchain/check/cpp/import.h" @@ -345,10 +346,22 @@ auto PerformCallToCppTemplateName(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclId template_decl_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { + auto args_id = context.inst_blocks().Add(arg_ids); + return HandleAction( + context, loc_id, SemIR::TypeInstId::None, + {.type_id = SemIR::InstType::TypeId, + .template_decl_id = template_decl_id, + .args_id = args_id}); +} + +auto PerformAction(Context& context, SemIR::LocId loc_id, + SemIR::CallCppTemplateAction action) -> SemIR::InstId { auto* template_decl = dyn_cast( - context.clang_decls().Get(template_decl_id).decl()); + context.clang_decls().Get(action.template_decl_id).decl()); auto loc = GetCppLocation(context, loc_id); + auto arg_ids = context.inst_blocks().Get(action.args_id); + // Form a template argument list for this template. clang::TemplateArgumentListInfo arg_list(loc, loc); if (!ConvertArgsToTemplateArgs(context, template_decl, arg_ids, arg_list)) { diff --git a/toolchain/check/testdata/interop/cpp/class/import/template.carbon b/toolchain/check/testdata/interop/cpp/class/import/template.carbon index f46e43985afd..86c5f60c711b 100644 --- a/toolchain/check/testdata/interop/cpp/class/import/template.carbon +++ b/toolchain/check/testdata/interop/cpp/class/import/template.carbon @@ -98,6 +98,26 @@ fn F() { v.begin(); } +// --- use_carbon_class_generic_in_cpp_template_param.carbon + +library "[[@TEST_NAME]]"; +import Cpp inline ''' +template +struct A { + T t; +}; +'''; + +//@dump-sem-ir-begin +class B(T: type) { + var a: Cpp.A(T); +} + +fn F(ref b: B(i32)) { + b.a.t = 123; +} +//@dump-sem-ir-end + // CHECK:STDOUT: --- use_class_template.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -272,3 +292,164 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- use_carbon_class_generic_in_cpp_template_param.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %.Self.frozen: %type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %B.type: type = generic_class_type @B [concrete] +// CHECK:STDOUT: %B.generic: %B.type = struct_value () [concrete] +// CHECK:STDOUT: %B.60e: type = class_type @B, @B(%T) [symbolic] +// CHECK:STDOUT: %.ae6: @B.%.loc12_17.2 (@B.%.loc12_17.2) = splice_inst @B.%.loc12_17.1 [template] +// CHECK:STDOUT: %.535: type = splice_inst @B.%.loc12_17.4 [template] +// CHECK:STDOUT: %require_complete.01e: = require_complete_type %.535 [template] +// CHECK:STDOUT: %pattern_type.281: type = pattern_type %.535 [template] +// CHECK:STDOUT: %a.patt.438: %pattern_type.281 = ref_binding_pattern a [template] +// CHECK:STDOUT: %B.elem.7af: type = unbound_element_type %B.60e, %.535 [template] +// CHECK:STDOUT: %struct_type.a.be9: type = struct_type {.a: %.535} [template] +// CHECK:STDOUT: %complete_type.7ab: = complete_type_witness %struct_type.a.be9 [template] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %B.bd8: type = class_type @B, @B(%i32) [concrete] +// CHECK:STDOUT: %pattern_type.fdc: type = pattern_type %B.bd8 [concrete] +// CHECK:STDOUT: %b.param_patt: %pattern_type.fdc = ref_param_pattern [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.fdc = wrapper_binding_pattern b, %b.param_patt [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %A: type = class_type @A [concrete] +// CHECK:STDOUT: %inst.splice_block.72d: = inst_value [concrete] { +// CHECK:STDOUT: %.22e: type = splice_block imports.%A.decl [concrete = %A] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: %inst.splice_block.c26: = inst_value [concrete] { +// CHECK:STDOUT: %.4e4: type = splice_block %A [concrete = %A] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.elem: type = unbound_element_type %A, %i32 [concrete] +// CHECK:STDOUT: %.ae8: type = custom_layout_type {size=4, align=4, .t@0: %i32} [concrete] +// CHECK:STDOUT: %complete_type.ece: = complete_type_witness %.ae8 [concrete] +// CHECK:STDOUT: %pattern_type.b78: type = pattern_type %A [concrete] +// CHECK:STDOUT: %a.patt.fbf: %pattern_type.b78 = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %B.elem.1e0: type = unbound_element_type %B.bd8, %A [concrete] +// CHECK:STDOUT: %struct_type.a.b63: type = struct_type {.a: %A} [concrete] +// CHECK:STDOUT: %complete_type.594: = complete_type_witness %struct_type.a.b63 [concrete] +// CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.914: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.845: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.a2a: = impl_witness imports.%ImplicitAs.impl_witness_table.1aa, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.2ba: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.2ba = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.914 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.a2a) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.8eb: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.b7a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.8eb, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_123.2bf: %i32 = int_value 123 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} +// CHECK:STDOUT: %Core.import_ref.edf: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.845)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1aa = impl_witness_table (%Core.import_ref.edf), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %B.decl: %B.type = class_decl @B [concrete = constants.%B.generic] { +// CHECK:STDOUT: %T.patt.loc11_10.1: %pattern_type.98f = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_10.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc11_12.1: type = splice_block %.loc11_12.2 [concrete = type] { +// CHECK:STDOUT: %.Self.frozen: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen] +// CHECK:STDOUT: %.loc11_12.2: type = type_literal type [concrete = type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc11_10.2: type = symbolic_binding T, 0 [symbolic = %T.loc11_10.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %b.param_patt: %pattern_type.fdc = ref_param_pattern [concrete = constants.%b.param_patt] +// CHECK:STDOUT: %b.patt: %pattern_type.fdc = wrapper_binding_pattern b, %b.param_patt [concrete = constants.%b.patt] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %b.param: ref %B.bd8 = ref_param call_param0 +// CHECK:STDOUT: %.loc15: type = splice_block %B [concrete = constants.%B.bd8] { +// CHECK:STDOUT: %B.ref: %B.type = name_ref B, file.%B.decl [concrete = constants.%B.generic] +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %B: type = class_type @B, @B(constants.%i32) [concrete = constants.%B.bd8] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: ref %B.bd8 = wrapper_binding b, %b.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @B(%T.loc11_10.2: type) { +// CHECK:STDOUT: %T.patt.loc11_10.2: %pattern_type.98f = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_10.2 (constants.%T.patt)] +// CHECK:STDOUT: %T.loc11_10.1: type = symbolic_binding T, 0 [symbolic = %T.loc11_10.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %.loc12_17.1: = call_template_action clang_decl_id4C000002, (%T.loc11_10.1) [template] +// CHECK:STDOUT: %.loc12_17.2: type = type_of_inst %.loc12_17.1 [template] +// CHECK:STDOUT: %.loc12_17.3: @B.%.loc12_17.2 (@B.%.loc12_17.2) = splice_inst %.loc12_17.1 [template = %.loc12_17.3 (constants.%.ae6)] +// CHECK:STDOUT: %.loc12_17.4: = convert_to_value_action .inst{{[0-9A-F]+}}.loc12_17, type [template] +// CHECK:STDOUT: %.loc12_17.5: type = splice_inst %.loc12_17.4 [template = %.loc12_17.5 (constants.%.535)] +// CHECK:STDOUT: %require_complete: = require_complete_type %.loc12_17.5 [template = %require_complete (constants.%require_complete.01e)] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.loc12_17.5 [template = %pattern_type (constants.%pattern_type.281)] +// CHECK:STDOUT: %a.patt: @B.%pattern_type (%pattern_type.281) = ref_binding_pattern a [template = %a.patt (constants.%a.patt.438)] +// CHECK:STDOUT: %B: type = class_type @B, @B(%T.loc11_10.1) [symbolic = %B (constants.%B.60e)] +// CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %.loc12_17.5 [template = %B.elem (constants.%B.elem.7af)] +// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: @B.%.loc12_17.5 (%.535)} [template = %struct_type.a (constants.%struct_type.a.be9)] +// CHECK:STDOUT: %complete_type.loc13_1.2: = complete_type_witness %struct_type.a [template = %complete_type.loc13_1.2 (constants.%complete_type.7ab)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %.loc12_8: @B.%B.elem (%B.elem.7af) = field_decl a, element0 [concrete] +// CHECK:STDOUT: %complete_type.loc13_1.1: = complete_type_witness constants.%struct_type.a.be9 [template = %complete_type.loc13_1.2 (constants.%complete_type.7ab)] +// CHECK:STDOUT: complete_type_witness = %complete_type.loc13_1.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%B.60e +// CHECK:STDOUT: .T = +// CHECK:STDOUT: .a = %.loc12_8 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%b.param: ref %B.bd8) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %b.ref: ref %B.bd8 = name_ref b, %b +// CHECK:STDOUT: %a.ref: %B.elem.1e0 = name_ref a, @B.%.loc12_8 [concrete = @B.%.loc12_8] +// CHECK:STDOUT: %.loc16_4: ref %A = class_element_access %b.ref, element0 +// CHECK:STDOUT: %t.ref: %A.elem = name_ref t, @A.%.1 [concrete = @A.%.1] +// CHECK:STDOUT: %.loc16_6: ref %i32 = class_element_access %.loc16_4, element0 +// CHECK:STDOUT: %int_123: Core.IntLiteral = int_value 123 [concrete = constants.%int_123.fff] +// CHECK:STDOUT: %impl.elem0: %.b7a = impl_witness_access constants.%ImplicitAs.impl_witness.a2a, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e39] +// CHECK:STDOUT: %bound_method.loc16_9.1: = bound_method %int_123, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc16_9.2: = bound_method %int_123, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc16_9.2(%int_123) [concrete = constants.%int_123.2bf] +// CHECK:STDOUT: %.loc16_9: init %i32 = converted %int_123, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_123.2bf] +// CHECK:STDOUT: assign %.loc16_6, %.loc16_9 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B(constants.%T) { +// CHECK:STDOUT: %T.patt.loc11_10.2 => constants.%T.patt +// CHECK:STDOUT: %T.loc11_10.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B(constants.%i32) { +// CHECK:STDOUT: %T.patt.loc11_10.2 => constants.%T.patt +// CHECK:STDOUT: %T.loc11_10.1 => constants.%i32 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %.loc12_17.1 => constants.%inst.splice_block.72d +// CHECK:STDOUT: %.loc12_17.2 => type +// CHECK:STDOUT: %.loc12_17.3 => constants.%A +// CHECK:STDOUT: %.loc12_17.4 => constants.%inst.splice_block.c26 +// CHECK:STDOUT: %.loc12_17.5 => constants.%A +// CHECK:STDOUT: %require_complete => constants.%complete_type.ece +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.b78 +// CHECK:STDOUT: %a.patt => constants.%a.patt.fbf +// CHECK:STDOUT: %B => constants.%B.bd8 +// CHECK:STDOUT: %B.elem => constants.%B.elem.1e0 +// CHECK:STDOUT: %struct_type.a => constants.%struct_type.a.b63 +// CHECK:STDOUT: %complete_type.loc13_1.2 => constants.%complete_type.594 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/generic_call.carbon b/toolchain/check/testdata/interop/cpp/template/generic_call.carbon index 78a7f4f199f7..e7ad2dd09625 100644 --- a/toolchain/check/testdata/interop/cpp/template/generic_call.carbon +++ b/toolchain/check/testdata/interop/cpp/template/generic_call.carbon @@ -26,9 +26,13 @@ import Cpp library "header.h"; fn F[T: type](unused x: T) { // TODO: This should be treated as being template-dependent on T, // and we should perform the call during monomorphization. - // CHECK:STDERR: fail_todo_generic_call.carbon:[[@LINE+4]]:23: error: semantics TODO: `unsupported type used as template argument` [SemanticsTodo] + // CHECK:STDERR: fail_todo_generic_call.carbon:[[@LINE+8]]:3: error: value of type `` is not callable [CallToNonCallable] // CHECK:STDERR: var unused v: Cpp.S(T); - // CHECK:STDERR: ^ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_generic_call.carbon:[[@LINE+4]]:3: error: value of type `` is not callable [CallToNonCallable] + // CHECK:STDERR: var unused v: Cpp.S(T); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: Cpp.S(T); } diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index cb3cea8186fc..f19f917f7ea4 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -1716,6 +1716,8 @@ auto Formatter::FormatArg(StringLiteralValueId id) -> void { << '"'; } +auto Formatter::FormatArg(ClangDeclId id) -> void { out() << id; } + auto Formatter::FormatReturnSlotArg(InstId dest_id) -> void { if (dest_id.has_value()) { out() << " to "; diff --git a/toolchain/sem_ir/formatter.h b/toolchain/sem_ir/formatter.h index 82f11f8796c0..c1c7c73cdb33 100644 --- a/toolchain/sem_ir/formatter.h +++ b/toolchain/sem_ir/formatter.h @@ -287,6 +287,7 @@ class Formatter { auto FormatArg(RealId id) -> void; auto FormatArg(StringLiteralValueId id) -> void; auto FormatArg(ConstantId id) -> void { FormatConstant(id); } + auto FormatArg(ClangDeclId id) -> void; template auto FormatArg(BundleId bundle_id) -> void { diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index a7d84167b349..040d54370a32 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -43,6 +43,7 @@ CARBON_SEM_IR_INST_KIND(Branch) CARBON_SEM_IR_INST_KIND(BranchIf) CARBON_SEM_IR_INST_KIND(BranchWithArg) CARBON_SEM_IR_INST_KIND(Call) +CARBON_SEM_IR_INST_KIND(CallCppTemplateAction) CARBON_SEM_IR_INST_KIND(CalleePatternMatchAction) CARBON_SEM_IR_INST_KIND(CallerPatternMatchAction) CARBON_SEM_IR_INST_KIND(CharLiteralType) diff --git a/toolchain/sem_ir/type_iterator.cpp b/toolchain/sem_ir/type_iterator.cpp index 5d81525c0166..41cdd99803fd 100644 --- a/toolchain/sem_ir/type_iterator.cpp +++ b/toolchain/sem_ir/type_iterator.cpp @@ -114,6 +114,7 @@ auto TypeIterator::ProcessType(InstId inst_id) -> std::optional { } case Call::Kind: + case SpliceInst::Kind: case TypeOfInst::Kind: { return Step::TemplateType(); } diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index d60c8745aebc..3f43ddf19e09 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -390,6 +390,21 @@ struct Call { InstBlockId args_id; }; +// An action that performs a C++ template call. +struct CallCppTemplateAction { + static constexpr auto Kind = + InstKind::CallCppTemplateAction.Define( + {.ir_name = "call_template_action", + .expr_category = ActionExprCategory(ExprCategory::Value), + .constant_kind = InstConstantKind::InstAction, + .is_lowered = false}); + + TypeId type_id; + ClangDeclId template_decl_id; + // Template arguments. + InstBlockId args_id; +}; + // An action that performs callee-side pattern matching for a single syntactic // parameter. struct CalleePatternMatchAction { @@ -1984,6 +1999,7 @@ struct SpliceInst { static constexpr auto Kind = InstKind::SpliceInst.Define( {.ir_name = "splice_inst", .expr_category = ComputedExprCategory::DependsOnOperands, + .is_type = InstIsType::Maybe, .constant_kind = InstConstantKind::Indirect}); TypeId type_id;