export.cpp 24 KB

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