export.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/check/cpp/export.h"
  5. #include "toolchain/check/cpp/import.h"
  6. #include "toolchain/check/cpp/location.h"
  7. #include "toolchain/check/cpp/type_mapping.h"
  8. #include "toolchain/check/function.h"
  9. #include "toolchain/check/thunk.h"
  10. #include "toolchain/check/type.h"
  11. #include "toolchain/sem_ir/mangler.h"
  12. namespace Carbon::Check {
  13. // If the given name scope was produced by importing a C++ declaration or has
  14. // already been exported to C++, return the corresponding Clang decl context.
  15. static auto GetClangDeclContextForScope(Context& context,
  16. SemIR::NameScopeId scope_id)
  17. -> clang::DeclContext* {
  18. if (!scope_id.has_value()) {
  19. return nullptr;
  20. }
  21. auto& scope = context.name_scopes().Get(scope_id);
  22. auto clang_decl_context_id = scope.clang_decl_context_id();
  23. if (!clang_decl_context_id.has_value()) {
  24. return nullptr;
  25. }
  26. auto* decl = context.clang_decls().Get(clang_decl_context_id).key.decl;
  27. return cast<clang::DeclContext>(decl);
  28. }
  29. auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
  30. SemIR::NameScopeId name_scope_id)
  31. -> clang::DeclContext* {
  32. llvm::SmallVector<SemIR::NameScopeId> name_scope_ids_to_create;
  33. // Walk through the parent scopes, looking for one that's already mapped into
  34. // C++. We already mapped the package scope to ::Carbon, so we must find one.
  35. clang::DeclContext* decl_context = nullptr;
  36. while (true) {
  37. // If this name scope was produced by importing a C++ declaration or has
  38. // already been exported to C++, return the corresponding Clang declaration.
  39. if (auto* existing_decl_context =
  40. GetClangDeclContextForScope(context, name_scope_id)) {
  41. decl_context = existing_decl_context;
  42. break;
  43. }
  44. // Otherwise, continue to the parent and create a scope for it first.
  45. name_scope_ids_to_create.push_back(name_scope_id);
  46. name_scope_id = context.name_scopes().Get(name_scope_id).parent_scope_id();
  47. // TODO: What should happen if there's an intervening function scope?
  48. CARBON_CHECK(
  49. name_scope_id.has_value(),
  50. "Reached the top level without finding a scope mapped into C++");
  51. }
  52. // Create the name scopes in order, starting from the outermost one.
  53. while (!name_scope_ids_to_create.empty()) {
  54. name_scope_id = name_scope_ids_to_create.pop_back_val();
  55. auto& name_scope = context.name_scopes().Get(name_scope_id);
  56. auto* identifier_info =
  57. GetClangIdentifierInfo(context, name_scope.name_id());
  58. if (!identifier_info) {
  59. // TODO: Handle keyword package names like `Cpp` and `Core`. These can
  60. // be named from C++ via an alias.
  61. context.TODO(loc_id, "interop with non-identifier package name");
  62. return nullptr;
  63. }
  64. auto inst = context.insts().Get(name_scope.inst_id());
  65. if (inst.Is<SemIR::Namespace>()) {
  66. // TODO: Provide a source location.
  67. auto* namespace_decl = clang::NamespaceDecl::Create(
  68. context.ast_context(), decl_context, false, clang::SourceLocation(),
  69. clang::SourceLocation(), identifier_info, nullptr, false);
  70. decl_context->addHiddenDecl(namespace_decl);
  71. decl_context = namespace_decl;
  72. } else if (inst.Is<SemIR::ClassDecl>()) {
  73. // TODO: Provide a source location.
  74. auto* record_decl = clang::CXXRecordDecl::Create(
  75. context.ast_context(), clang::TagTypeKind::Class, decl_context,
  76. clang::SourceLocation(), clang::SourceLocation(), identifier_info);
  77. // If this is a member class, set its access.
  78. if (isa<clang::CXXRecordDecl>(decl_context)) {
  79. // TODO: Map Carbon access to C++ access.
  80. record_decl->setAccess(clang::AS_public);
  81. }
  82. decl_context->addHiddenDecl(record_decl);
  83. decl_context = record_decl;
  84. decl_context->setHasExternalLexicalStorage();
  85. } else {
  86. context.TODO(loc_id, "non-class non-namespace name scope");
  87. return nullptr;
  88. }
  89. decl_context->setHasExternalVisibleStorage();
  90. auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
  91. cast<clang::Decl>(decl_context));
  92. auto clang_decl_id = context.clang_decls().Add(
  93. {.key = key, .inst_id = name_scope.inst_id()});
  94. name_scope.set_clang_decl_context_id(clang_decl_id, /*is_cpp_scope=*/false);
  95. }
  96. return decl_context;
  97. }
  98. auto ExportClassToCpp(Context& context, SemIR::LocId loc_id,
  99. SemIR::InstId class_inst_id, SemIR::ClassType class_type)
  100. -> clang::TagDecl* {
  101. // TODO: A lot of logic in this function is shared with ExportNameScopeToCpp.
  102. // This should be refactored.
  103. if (class_type.specific_id.has_value()) {
  104. context.TODO(loc_id, "interop with specific class");
  105. return nullptr;
  106. }
  107. const auto& class_info = context.classes().Get(class_type.class_id);
  108. // If this class was produced by importing a C++ declaration or has
  109. // already been exported to C++, return the corresponding Clang declaration.
  110. // That could either be a CXXRecordDecl or an EnumDecl.
  111. if (auto* decl_context =
  112. GetClangDeclContextForScope(context, class_info.scope_id)) {
  113. return cast<clang::TagDecl>(decl_context);
  114. }
  115. auto* identifier_info = GetClangIdentifierInfo(context, class_info.name_id);
  116. CARBON_CHECK(identifier_info, "non-identifier class name {0}",
  117. class_info.name_id);
  118. auto* decl_context =
  119. ExportNameScopeToCpp(context, loc_id, class_info.parent_scope_id);
  120. // TODO: Provide a source location.
  121. auto* record_decl = clang::CXXRecordDecl::Create(
  122. context.ast_context(), clang::TagTypeKind::Class, decl_context,
  123. clang::SourceLocation(), clang::SourceLocation(), identifier_info);
  124. // If this is a member class, set its access.
  125. if (isa<clang::CXXRecordDecl>(decl_context)) {
  126. // TODO: Map Carbon access to C++ access.
  127. record_decl->setAccess(clang::AS_public);
  128. }
  129. record_decl->setHasExternalLexicalStorage();
  130. record_decl->setHasExternalVisibleStorage();
  131. auto key =
  132. SemIR::ClangDeclKey::ForNonFunctionDecl(cast<clang::Decl>(record_decl));
  133. auto clang_decl_id =
  134. context.clang_decls().Add({.key = key, .inst_id = class_inst_id});
  135. if (class_info.scope_id.has_value()) {
  136. // TODO: Record the Carbon class -> clang declaration mapping for incomplete
  137. // classes too.
  138. context.name_scopes()
  139. .Get(class_info.scope_id)
  140. .set_clang_decl_context_id(clang_decl_id, /*is_cpp_scope=*/false);
  141. }
  142. return record_decl;
  143. }
  144. // Create a `clang::FunctionDecl` for the given Carbon function. This
  145. // can be used to call the Carbon function from C++. The Carbon
  146. // function's ABI must be compatible with C++.
  147. //
  148. // The resulting decl is used to allow a generated C++ function to call
  149. // a generated Carbon function.
  150. static auto BuildCppFunctionDeclForCarbonFn(Context& context,
  151. SemIR::LocId loc_id,
  152. SemIR::FunctionId function_id)
  153. -> clang::FunctionDecl* {
  154. auto clang_loc = GetCppLocation(context, loc_id);
  155. const SemIR::Function& function = context.functions().Get(function_id);
  156. // Get parameters types.
  157. auto carbon_function_params =
  158. context.inst_blocks().Get(function.call_param_patterns_id);
  159. llvm::SmallVector<clang::QualType> cpp_param_types;
  160. for (auto param_inst_id : carbon_function_params) {
  161. auto scrutinee_type_id = ExtractScrutineeType(
  162. context.sem_ir(), context.insts().Get(param_inst_id).type_id());
  163. auto cpp_type = MapToCppType(context, scrutinee_type_id);
  164. if (cpp_type.isNull()) {
  165. context.TODO(loc_id, "failed to map Carbon type to C++");
  166. return nullptr;
  167. }
  168. auto ref_type = context.ast_context().getLValueReferenceType(cpp_type);
  169. cpp_param_types.push_back(ref_type);
  170. }
  171. CARBON_CHECK(function.return_type_inst_id == SemIR::TypeInstId::None);
  172. auto cpp_return_type = context.ast_context().VoidTy;
  173. auto cpp_function_type = context.ast_context().getFunctionType(
  174. cpp_return_type, cpp_param_types,
  175. clang::FunctionProtoType::ExtProtoInfo());
  176. auto* identifier_info = GetClangIdentifierInfo(context, function.name_id);
  177. CARBON_CHECK(identifier_info, "function with non-identifier name {0}",
  178. function.name_id);
  179. auto* function_decl = clang::FunctionDecl::Create(
  180. context.ast_context(), context.ast_context().getTranslationUnitDecl(),
  181. /*StartLoc=*/clang_loc, /*NLoc=*/clang_loc, identifier_info,
  182. cpp_function_type, /*TInfo=*/nullptr, clang::SC_Extern);
  183. // Build parameter decls.
  184. llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
  185. for (auto [i, type] : llvm::enumerate(cpp_param_types)) {
  186. clang::ParmVarDecl* param = clang::ParmVarDecl::Create(
  187. context.ast_context(), function_decl, /*StartLoc=*/clang_loc,
  188. /*IdLoc=*/clang_loc, /*Id=*/nullptr, type, /*TInfo=*/nullptr,
  189. clang::SC_None, /*DefArg=*/nullptr);
  190. param_var_decls.push_back(param);
  191. }
  192. function_decl->setParams(param_var_decls);
  193. // Mangle the function name and attach it to the `FunctionDecl`.
  194. SemIR::Mangler m(context.sem_ir(), context.total_ir_count());
  195. std::string mangled_name = m.Mangle(function_id, SemIR::SpecificId::None);
  196. function_decl->addAttr(
  197. clang::AsmLabelAttr::Create(context.ast_context(), mangled_name));
  198. return function_decl;
  199. }
  200. // Create the declaration of the C++ thunk.
  201. static auto BuildCppToCarbonThunkDecl(
  202. Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context,
  203. clang::DeclarationName thunk_name,
  204. llvm::ArrayRef<clang::QualType> thunk_param_types,
  205. SemIR::TypeId return_type_id) -> clang::FunctionDecl* {
  206. clang::ASTContext& ast_context = context.ast_context();
  207. auto clang_loc = GetCppLocation(context, loc_id);
  208. // Get the C++ return type (this corresponds to the return type of the
  209. // target Carbon function).
  210. clang::QualType cpp_return_type = context.ast_context().VoidTy;
  211. if (return_type_id != SemIR::TypeId::None) {
  212. cpp_return_type = MapToCppType(context, return_type_id);
  213. if (cpp_return_type.isNull()) {
  214. context.TODO(loc_id, "failed to map Carbon return type to C++ type");
  215. return nullptr;
  216. }
  217. }
  218. clang::DeclarationNameInfo name_info(thunk_name, clang_loc);
  219. auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
  220. clang::QualType thunk_function_type = ast_context.getFunctionType(
  221. cpp_return_type, thunk_param_types, ext_proto_info);
  222. auto* tinfo =
  223. ast_context.getTrivialTypeSourceInfo(thunk_function_type, clang_loc);
  224. bool uses_fp_intrin = false;
  225. bool inline_specified = true;
  226. auto constexpr_kind = clang::ConstexprSpecKind::Unspecified;
  227. auto trailing_requires_clause = clang::AssociatedConstraint();
  228. clang::FunctionDecl* thunk_function_decl = nullptr;
  229. if (auto* parent_class = dyn_cast<clang::CXXRecordDecl>(decl_context)) {
  230. // TODO: Support non-static methods.
  231. thunk_function_decl = clang::CXXMethodDecl::Create(
  232. ast_context, parent_class, clang_loc, name_info, thunk_function_type,
  233. tinfo, clang::SC_Static, uses_fp_intrin, inline_specified,
  234. constexpr_kind, clang_loc, trailing_requires_clause);
  235. // TODO: Map Carbon access to C++ access.
  236. thunk_function_decl->setAccess(clang::AS_public);
  237. } else {
  238. thunk_function_decl = clang::FunctionDecl::Create(
  239. ast_context, decl_context, clang_loc, name_info, thunk_function_type,
  240. tinfo, clang::SC_None, uses_fp_intrin, inline_specified,
  241. /*hasWrittenPrototype=*/true, constexpr_kind, trailing_requires_clause);
  242. }
  243. decl_context->addDecl(thunk_function_decl);
  244. llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
  245. for (auto [i, type] : llvm::enumerate(thunk_param_types)) {
  246. clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
  247. ast_context, thunk_function_decl, /*StartLoc=*/clang_loc,
  248. /*IdLoc=*/clang_loc, /*Id=*/nullptr, type,
  249. /*TInfo=*/nullptr, clang::SC_None, /*DefArg=*/nullptr);
  250. param_var_decls.push_back(thunk_param);
  251. }
  252. thunk_function_decl->setParams(param_var_decls);
  253. // Force the thunk to be inlined and discarded.
  254. thunk_function_decl->addAttr(
  255. clang::AlwaysInlineAttr::CreateImplicit(ast_context));
  256. thunk_function_decl->addAttr(
  257. clang::InternalLinkageAttr::CreateImplicit(ast_context));
  258. return thunk_function_decl;
  259. }
  260. // Create the body of a C++ thunk that calls a Carbon thunk. The
  261. // arguments are passed by reference to the callee.
  262. static auto BuildCppToCarbonThunkBody(clang::Sema& sema,
  263. clang::FunctionDecl* function_decl,
  264. clang::FunctionDecl* callee_function_decl)
  265. -> clang::StmtResult {
  266. clang::SourceLocation clang_loc = function_decl->getLocation();
  267. llvm::SmallVector<clang::Stmt*> stmts;
  268. // Create return storage if the target function returns non-void.
  269. const bool has_return_value = !function_decl->getReturnType()->isVoidType();
  270. clang::VarDecl* return_storage_var_decl = nullptr;
  271. clang::ExprResult return_storage_expr;
  272. if (has_return_value) {
  273. auto& return_storage_ident =
  274. sema.getASTContext().Idents.get("return_storage");
  275. return_storage_var_decl =
  276. clang::VarDecl::Create(sema.getASTContext(), function_decl,
  277. /*StartLoc=*/clang_loc,
  278. /*IdLoc=*/clang_loc, &return_storage_ident,
  279. function_decl->getReturnType(),
  280. /*TInfo=*/nullptr, clang::SC_None);
  281. return_storage_var_decl->setNRVOVariable(true);
  282. return_storage_expr = sema.BuildDeclRefExpr(
  283. return_storage_var_decl, return_storage_var_decl->getType(),
  284. clang::VK_LValue, clang_loc);
  285. auto decl_group_ref = clang::DeclGroupRef(return_storage_var_decl);
  286. auto decl_stmt =
  287. sema.ActOnDeclStmt(clang::Sema::DeclGroupPtrTy::make(decl_group_ref),
  288. clang_loc, clang_loc);
  289. stmts.push_back(decl_stmt.get());
  290. }
  291. clang::ExprResult callee = sema.BuildDeclRefExpr(
  292. callee_function_decl, callee_function_decl->getType(), clang::VK_PRValue,
  293. clang_loc);
  294. llvm::SmallVector<clang::Expr*> call_args;
  295. for (auto* param : function_decl->parameters()) {
  296. clang::Expr* call_arg =
  297. sema.BuildDeclRefExpr(param, param->getType().getNonReferenceType(),
  298. clang::VK_LValue, clang_loc);
  299. call_args.push_back(call_arg);
  300. }
  301. // If the target function returns non-void, the Carbon thunk takes an
  302. // extra output parameter referencing the return storage.
  303. if (has_return_value) {
  304. call_args.push_back(return_storage_expr.get());
  305. }
  306. clang::ExprResult call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc,
  307. call_args, clang_loc);
  308. CARBON_CHECK(call.isUsable());
  309. stmts.push_back(call.get());
  310. if (has_return_value) {
  311. auto* return_stmt = clang::ReturnStmt::Create(
  312. sema.getASTContext(), clang_loc, return_storage_expr.get(),
  313. return_storage_var_decl);
  314. stmts.push_back(return_stmt);
  315. }
  316. return clang::CompoundStmt::Create(sema.getASTContext(), stmts,
  317. clang::FPOptionsOverride(), clang_loc,
  318. clang_loc);
  319. }
  320. // Create a C++ thunk that calls the Carbon thunk. The C++ thunk's
  321. // parameter types are mapped from the parameters of the target function
  322. // with `MapToCppType`. (Note that the target function here is the
  323. // callee of the Carbon thunk.)
  324. static auto BuildCppToCarbonThunk(
  325. Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context,
  326. llvm::StringRef base_name, clang::FunctionDecl* carbon_function_decl,
  327. llvm::ArrayRef<SemIR::TypeId> callee_param_type_ids,
  328. SemIR::TypeId return_type_id) -> clang::FunctionDecl* {
  329. // Create the thunk's name.
  330. llvm::SmallString<64> thunk_name = base_name;
  331. thunk_name += "__cpp_thunk";
  332. auto& thunk_ident = context.ast_context().Idents.get(thunk_name);
  333. llvm::SmallVector<clang::QualType> param_types;
  334. for (auto type_id : callee_param_type_ids) {
  335. auto cpp_type = MapToCppType(context, type_id);
  336. if (cpp_type.isNull()) {
  337. context.TODO(loc_id, "failed to map C++ type to Carbon");
  338. return nullptr;
  339. }
  340. param_types.push_back(cpp_type);
  341. }
  342. auto* thunk_function_decl = BuildCppToCarbonThunkDecl(
  343. context, loc_id, decl_context, &thunk_ident, param_types, return_type_id);
  344. // Build the thunk function body.
  345. clang::Sema& sema = context.clang_sema();
  346. clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
  347. sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
  348. clang::StmtResult body = BuildCppToCarbonThunkBody(sema, thunk_function_decl,
  349. carbon_function_decl);
  350. sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
  351. CARBON_CHECK(!body.isInvalid());
  352. context.clang_sema().getASTConsumer().HandleTopLevelDecl(
  353. clang::DeclGroupRef(thunk_function_decl));
  354. return thunk_function_decl;
  355. }
  356. // Create a Carbon thunk that calls `callee`. The thunk's parameters are
  357. // all references to the callee parameter type.
  358. static auto BuildCarbonToCarbonThunk(
  359. Context& context, SemIR::LocId loc_id, SemIR::FunctionId callee_function_id,
  360. const SemIR::Function& callee,
  361. llvm::ArrayRef<SemIR::TypeId> callee_param_type_ids) -> SemIR::FunctionId {
  362. // Create the thunk's name.
  363. llvm::SmallString<64> thunk_name =
  364. context.names().GetFormatted(callee.name_id);
  365. thunk_name += "__carbon_thunk";
  366. auto& ident = context.ast_context().Idents.get(thunk_name);
  367. auto thunk_name_id =
  368. SemIR::NameId::ForIdentifier(context.identifiers().Add(ident.getName()));
  369. // Get the thunk's parameters. These match the callee parameters, with
  370. // the addition of an output parameter for the callee's return value
  371. // (if it has one).
  372. llvm::SmallVector<SemIR::TypeId> thunk_param_type_ids(callee_param_type_ids);
  373. auto callee_return_type_id = callee.GetDeclaredReturnType(context.sem_ir());
  374. if (callee_return_type_id != SemIR::TypeId::None) {
  375. thunk_param_type_ids.push_back(callee_return_type_id);
  376. }
  377. auto carbon_thunk_function_id =
  378. MakeGeneratedFunctionDecl(context, loc_id,
  379. {.parent_scope_id = callee.parent_scope_id,
  380. .name_id = thunk_name_id,
  381. .param_type_ids = thunk_param_type_ids,
  382. .params_are_refs = true})
  383. .second;
  384. BuildThunkDefinitionForExport(
  385. context, carbon_thunk_function_id, callee_function_id,
  386. context.functions().Get(carbon_thunk_function_id).first_decl_id(),
  387. callee.first_decl_id());
  388. return carbon_thunk_function_id;
  389. }
  390. auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
  391. SemIR::FunctionId callee_function_id)
  392. -> clang::FunctionDecl* {
  393. const SemIR::Function& callee = context.functions().Get(callee_function_id);
  394. if (callee.generic_id.has_value()) {
  395. context.TODO(loc_id,
  396. "unsupported: C++ calling a Carbon function with "
  397. "generic parameters");
  398. return nullptr;
  399. }
  400. if (callee.call_param_ranges.implicit_size() != 0) {
  401. context.TODO(loc_id,
  402. "unsupported: C++ calling a Carbon function with "
  403. "an implicit parameter");
  404. return nullptr;
  405. }
  406. // Map the parent scope into the C++ AST.
  407. auto* decl_context =
  408. ExportNameScopeToCpp(context, loc_id, callee.parent_scope_id);
  409. if (!decl_context) {
  410. return nullptr;
  411. }
  412. // Get the parameter types of the Carbon function being
  413. // called. Exclude return params, if present.
  414. auto callee_function_params =
  415. context.inst_blocks().Get(callee.call_param_patterns_id);
  416. callee_function_params =
  417. callee_function_params.drop_back(callee.call_param_ranges.return_size());
  418. llvm::SmallVector<SemIR::TypeId> callee_param_type_ids;
  419. for (auto callee_param_inst_id : callee_function_params) {
  420. auto scrutinee_type_id = ExtractScrutineeType(
  421. context.sem_ir(), context.insts().Get(callee_param_inst_id).type_id());
  422. callee_param_type_ids.push_back(scrutinee_type_id);
  423. }
  424. // Create a Carbon thunk that calls the callee. The thunk's parameters
  425. // are all references so that the ABI is compatible with C++ callers.
  426. auto carbon_thunk_function_id = BuildCarbonToCarbonThunk(
  427. context, loc_id, callee_function_id, callee, callee_param_type_ids);
  428. // Create a `clang::FunctionDecl` that can be used to call the Carbon thunk.
  429. auto* carbon_function_decl = BuildCppFunctionDeclForCarbonFn(
  430. context, loc_id, carbon_thunk_function_id);
  431. if (!carbon_function_decl) {
  432. return nullptr;
  433. }
  434. // Create a C++ thunk that calls the Carbon thunk.
  435. return BuildCppToCarbonThunk(context, loc_id, decl_context,
  436. context.names().GetFormatted(callee.name_id),
  437. carbon_function_decl, callee_param_type_ids,
  438. callee.GetDeclaredReturnType(context.sem_ir()));
  439. }
  440. } // namespace Carbon::Check