export.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  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 <optional>
  6. #include "llvm/Support/Casting.h"
  7. #include "toolchain/check/cpp/import.h"
  8. #include "toolchain/check/cpp/location.h"
  9. #include "toolchain/check/cpp/type_mapping.h"
  10. #include "toolchain/check/function.h"
  11. #include "toolchain/check/pattern.h"
  12. #include "toolchain/check/thunk.h"
  13. #include "toolchain/check/type.h"
  14. #include "toolchain/sem_ir/mangler.h"
  15. namespace Carbon::Check {
  16. // If the given name scope was produced by importing a C++ declaration or has
  17. // already been exported to C++, return the corresponding Clang decl context.
  18. static auto GetClangDeclContextForScope(Context& context,
  19. SemIR::NameScopeId scope_id)
  20. -> clang::DeclContext* {
  21. if (!scope_id.has_value()) {
  22. return nullptr;
  23. }
  24. auto& scope = context.name_scopes().Get(scope_id);
  25. auto clang_decl_context_id = scope.clang_decl_context_id();
  26. if (!clang_decl_context_id.has_value()) {
  27. return nullptr;
  28. }
  29. auto* decl = context.clang_decls().Get(clang_decl_context_id).key.decl;
  30. return cast<clang::DeclContext>(decl);
  31. }
  32. auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
  33. SemIR::NameScopeId name_scope_id)
  34. -> clang::DeclContext* {
  35. llvm::SmallVector<SemIR::NameScopeId> name_scope_ids_to_create;
  36. // Walk through the parent scopes, looking for one that's already mapped into
  37. // C++. We already mapped the package scope to ::Carbon, so we must find one.
  38. clang::DeclContext* decl_context = nullptr;
  39. while (true) {
  40. // If this name scope was produced by importing a C++ declaration or has
  41. // already been exported to C++, return the corresponding Clang declaration.
  42. if (auto* existing_decl_context =
  43. GetClangDeclContextForScope(context, name_scope_id)) {
  44. decl_context = existing_decl_context;
  45. break;
  46. }
  47. // Otherwise, continue to the parent and create a scope for it first.
  48. name_scope_ids_to_create.push_back(name_scope_id);
  49. name_scope_id = context.name_scopes().Get(name_scope_id).parent_scope_id();
  50. // TODO: What should happen if there's an intervening function scope?
  51. CARBON_CHECK(
  52. name_scope_id.has_value(),
  53. "Reached the top level without finding a scope mapped into C++");
  54. }
  55. // Create the name scopes in order, starting from the outermost one.
  56. while (!name_scope_ids_to_create.empty()) {
  57. name_scope_id = name_scope_ids_to_create.pop_back_val();
  58. auto& name_scope = context.name_scopes().Get(name_scope_id);
  59. auto* identifier_info =
  60. GetClangIdentifierInfo(context, name_scope.name_id());
  61. if (!identifier_info) {
  62. // TODO: Handle keyword package names like `Cpp` and `Core`. These can
  63. // be named from C++ via an alias.
  64. context.TODO(loc_id, "interop with non-identifier package name");
  65. return nullptr;
  66. }
  67. auto inst = context.insts().Get(name_scope.inst_id());
  68. if (inst.Is<SemIR::Namespace>()) {
  69. // TODO: Provide a source location.
  70. auto* namespace_decl = clang::NamespaceDecl::Create(
  71. context.ast_context(), decl_context, false, clang::SourceLocation(),
  72. clang::SourceLocation(), identifier_info, nullptr, false);
  73. decl_context->addHiddenDecl(namespace_decl);
  74. decl_context = namespace_decl;
  75. } else if (inst.Is<SemIR::ClassDecl>()) {
  76. // TODO: Provide a source location.
  77. auto* record_decl = clang::CXXRecordDecl::Create(
  78. context.ast_context(), clang::TagTypeKind::Class, decl_context,
  79. clang::SourceLocation(), clang::SourceLocation(), identifier_info);
  80. // If this is a member class, set its access.
  81. if (isa<clang::CXXRecordDecl>(decl_context)) {
  82. // TODO: Map Carbon access to C++ access.
  83. record_decl->setAccess(clang::AS_public);
  84. }
  85. decl_context->addHiddenDecl(record_decl);
  86. decl_context = record_decl;
  87. decl_context->setHasExternalLexicalStorage();
  88. } else {
  89. context.TODO(loc_id, "non-class non-namespace name scope");
  90. return nullptr;
  91. }
  92. decl_context->setHasExternalVisibleStorage();
  93. auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
  94. cast<clang::Decl>(decl_context));
  95. auto clang_decl_id = context.clang_decls().Add(
  96. {.key = key, .inst_id = name_scope.inst_id()});
  97. name_scope.set_clang_decl_context_id(clang_decl_id, /*is_cpp_scope=*/false);
  98. }
  99. return decl_context;
  100. }
  101. auto ExportClassToCpp(Context& context, SemIR::LocId loc_id,
  102. SemIR::InstId class_inst_id, SemIR::ClassType class_type)
  103. -> clang::TagDecl* {
  104. // TODO: A lot of logic in this function is shared with ExportNameScopeToCpp.
  105. // This should be refactored.
  106. if (class_type.specific_id.has_value()) {
  107. context.TODO(loc_id, "interop with specific class");
  108. return nullptr;
  109. }
  110. const auto& class_info = context.classes().Get(class_type.class_id);
  111. // If this class was produced by importing a C++ declaration or has
  112. // already been exported to C++, return the corresponding Clang declaration.
  113. // That could either be a CXXRecordDecl or an EnumDecl.
  114. if (auto* decl_context =
  115. GetClangDeclContextForScope(context, class_info.scope_id)) {
  116. return cast<clang::TagDecl>(decl_context);
  117. }
  118. auto* identifier_info = GetClangIdentifierInfo(context, class_info.name_id);
  119. CARBON_CHECK(identifier_info, "non-identifier class name {0}",
  120. class_info.name_id);
  121. auto* decl_context =
  122. ExportNameScopeToCpp(context, loc_id, class_info.parent_scope_id);
  123. // TODO: Provide a source location.
  124. auto* record_decl = clang::CXXRecordDecl::Create(
  125. context.ast_context(), clang::TagTypeKind::Class, decl_context,
  126. clang::SourceLocation(), clang::SourceLocation(), identifier_info);
  127. // If this is a member class, set its access.
  128. if (isa<clang::CXXRecordDecl>(decl_context)) {
  129. // TODO: Map Carbon access to C++ access.
  130. record_decl->setAccess(clang::AS_public);
  131. }
  132. record_decl->setHasExternalLexicalStorage();
  133. record_decl->setHasExternalVisibleStorage();
  134. auto key =
  135. SemIR::ClangDeclKey::ForNonFunctionDecl(cast<clang::Decl>(record_decl));
  136. auto clang_decl_id =
  137. context.clang_decls().Add({.key = key, .inst_id = class_inst_id});
  138. if (class_info.scope_id.has_value()) {
  139. // TODO: Record the Carbon class -> clang declaration mapping for incomplete
  140. // classes too.
  141. context.name_scopes()
  142. .Get(class_info.scope_id)
  143. .set_clang_decl_context_id(clang_decl_id, /*is_cpp_scope=*/false);
  144. }
  145. return record_decl;
  146. }
  147. // Get the `StructTypeField`s from a class's object repr.
  148. static auto GetStructTypeFields(Context& context,
  149. const SemIR::Class& class_info)
  150. -> llvm::ArrayRef<SemIR::StructTypeField> {
  151. if (class_info.adapt_id.has_value()) {
  152. // The representation of an adapter won't necessarily be a
  153. // struct. Return an empty array since adapters can't declare
  154. // fields.
  155. return {};
  156. }
  157. auto object_repr_type_id =
  158. class_info.GetObjectRepr(context.sem_ir(), SemIR::SpecificId::None);
  159. auto struct_type =
  160. context.types().GetAs<SemIR::StructType>(object_repr_type_id);
  161. return context.struct_type_fields().Get(struct_type.fields_id);
  162. }
  163. static auto LookupClassFieldByStructField(
  164. const Context& context, const SemIR::NameScope& class_scope,
  165. const SemIR::StructTypeField& struct_field)
  166. -> std::optional<SemIR::InstStore::GetAsWithIdResult<SemIR::FieldDecl>> {
  167. if (auto entry_id = class_scope.Lookup(struct_field.name_id)) {
  168. auto field_inst_id =
  169. class_scope.GetEntry(*entry_id).result.target_inst_id();
  170. return context.insts().TryGetAsWithId<SemIR::FieldDecl>(field_inst_id);
  171. }
  172. return std::nullopt;
  173. }
  174. // Creates a `clang::FieldDecl` for a Carbon class field. Returns
  175. // nullptr if an error occurs.
  176. static auto CreateCppFieldDecl(Context& context,
  177. clang::CXXRecordDecl* record_decl,
  178. SemIR::InstId field_inst_id,
  179. const SemIR::FieldDecl& field_decl)
  180. -> clang::FieldDecl* {
  181. // Get the field's C++ type.
  182. auto unbound_element_type =
  183. context.types().GetAs<SemIR::UnboundElementType>(field_decl.type_id);
  184. auto cpp_type =
  185. MapToCppType(context, context.types().GetTypeIdForTypeInstId(
  186. unbound_element_type.element_type_inst_id));
  187. if (cpp_type.isNull()) {
  188. context.TODO(field_inst_id, "failed to map Carbon type to C++");
  189. return nullptr;
  190. }
  191. // Get the field's C++ identifier.
  192. auto* identifier_info = GetClangIdentifierInfo(context, field_decl.name_id);
  193. CARBON_CHECK(identifier_info, "field with non-identifier name {0}",
  194. field_decl.name_id);
  195. // Create the `clang::FieldDecl`.
  196. auto clang_loc = GetCppLocation(context, SemIR::LocId(field_inst_id));
  197. auto* cpp_field_decl = clang::FieldDecl::Create(
  198. context.ast_context(), record_decl, /*StartLoc=*/clang_loc,
  199. /*IdLoc=*/clang_loc, identifier_info, cpp_type, /*TInfo=*/nullptr,
  200. /*BW=*/nullptr,
  201. /*Mutable=*/true, clang::ICIS_NoInit);
  202. cpp_field_decl->setAccess(clang::AS_public);
  203. record_decl->addHiddenDecl(cpp_field_decl);
  204. return cpp_field_decl;
  205. }
  206. auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void {
  207. if (class_info.fields_exported) {
  208. return;
  209. }
  210. const auto& class_scope = context.name_scopes().Get(class_info.scope_id);
  211. for (const auto& struct_field : GetStructTypeFields(context, class_info)) {
  212. auto class_field =
  213. LookupClassFieldByStructField(context, class_scope, struct_field);
  214. if (!class_field) {
  215. continue;
  216. }
  217. // Map the parent scope into the C++ AST.
  218. auto* decl_context = ExportNameScopeToCpp(
  219. context, SemIR::LocId(class_field->inst_id), class_info.scope_id);
  220. if (!decl_context) {
  221. continue;
  222. }
  223. auto* cpp_field_decl =
  224. CreateCppFieldDecl(context, cast<clang::CXXRecordDecl>(decl_context),
  225. class_field->inst_id, class_field->inst);
  226. if (!cpp_field_decl) {
  227. continue;
  228. }
  229. // Create and store the `ClangDeclId`.
  230. auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(cpp_field_decl);
  231. context.clang_decls().Add({.key = key, .inst_id = class_field->inst_id});
  232. }
  233. class_info.fields_exported = true;
  234. }
  235. auto ExportFieldToCpp(Context& context, SemIR::InstId field_inst_id,
  236. SemIR::FieldDecl field_decl) -> clang::FieldDecl* {
  237. // Get the `SemIR::Class` that contains the `field_decl`.
  238. auto unbound_element_type =
  239. context.types().GetAs<SemIR::UnboundElementType>(field_decl.type_id);
  240. SemIR::TypeId class_type_id = context.types().GetTypeIdForTypeInstId(
  241. unbound_element_type.class_type_inst_id);
  242. auto class_type = context.types().GetAs<SemIR::ClassType>(class_type_id);
  243. auto& class_info = context.classes().Get(class_type.class_id);
  244. // If the class's fields haven't already been exported, do so now.
  245. ExportAllFieldsToCpp(context, class_info);
  246. // Get the exported `clang::FieldDecl`.
  247. auto clang_decl_id = context.clang_decls().Lookup(field_inst_id);
  248. if (clang_decl_id == SemIR::ClangDeclId::None) {
  249. return nullptr;
  250. }
  251. return cast<clang::FieldDecl>(
  252. context.clang_decls().Get(clang_decl_id).key.decl);
  253. }
  254. auto CalculateCppFieldOffsets(
  255. Context& context, SemIR::ClassId class_id,
  256. llvm::DenseMap<const clang::FieldDecl*, uint64_t>& field_offsets) -> bool {
  257. auto class_info = context.classes().Get(class_id);
  258. const auto& class_scope = context.name_scopes().Get(class_info.scope_id);
  259. auto class_layout = SemIR::ObjectLayout::Empty();
  260. for (const auto& struct_field : GetStructTypeFields(context, class_info)) {
  261. auto field_type_id = context.sem_ir().types().GetTypeIdForTypeInstId(
  262. struct_field.type_inst_id);
  263. auto field_layout = context.sem_ir()
  264. .types()
  265. .GetCompleteTypeInfo(field_type_id)
  266. .object_layout;
  267. // Use the field's name to look up the corresponding entry in the
  268. // class. If it's a `FieldDecl`, write out the offset of the
  269. // corresponding `clang::FieldDecl`.
  270. auto class_field =
  271. LookupClassFieldByStructField(context, class_scope, struct_field);
  272. if (class_field) {
  273. auto* cpp_field_decl =
  274. ExportFieldToCpp(context, class_field->inst_id, class_field->inst);
  275. if (!cpp_field_decl) {
  276. return false;
  277. }
  278. field_offsets.insert(
  279. {cpp_field_decl, class_layout.FieldOffset(field_layout).bits()});
  280. }
  281. class_layout.AppendField(field_layout);
  282. }
  283. return true;
  284. }
  285. namespace {
  286. struct FunctionInfo {
  287. struct Param {
  288. Param(Context& context, SemIR::InstId param_inst_id)
  289. : type_id(ExtractScrutineeType(
  290. context.sem_ir(), context.insts().Get(param_inst_id).type_id())),
  291. is_ref(context.insts().Is<SemIR::RefParamPattern>(param_inst_id)) {}
  292. // Type of the parameter's scrutinee.
  293. SemIR::TypeId type_id;
  294. // Whether this is a `ref` param.
  295. bool is_ref;
  296. };
  297. explicit FunctionInfo(Context& context, SemIR::FunctionId function_id,
  298. const SemIR::Function& function,
  299. clang::DeclContext* decl_context)
  300. : function_id(function_id),
  301. function(function),
  302. decl_context(decl_context) {
  303. auto function_params =
  304. context.inst_blocks().Get(function.call_param_patterns_id);
  305. // Get the function's `self` parameter, if present.
  306. if (function.call_param_ranges.implicit_size() > 0) {
  307. CARBON_CHECK(function.call_param_ranges.implicit_size() == 1);
  308. auto param_inst_id =
  309. function_params[function.call_param_ranges.implicit_begin().index];
  310. self_param = Param(context, param_inst_id);
  311. }
  312. // Get the function's explicit parameters.
  313. function_params =
  314. function_params.drop_front(function.call_param_ranges.implicit_size());
  315. function_params =
  316. function_params.drop_back(function.call_param_ranges.return_size());
  317. for (auto param_inst_id : function_params) {
  318. explicit_params.push_back(Param(context, param_inst_id));
  319. }
  320. }
  321. // Get the `StorageClass` to use for `CXXMethodDecl`s.
  322. auto GetStorageClass() const -> clang::StorageClass {
  323. if (self_param) {
  324. return clang::SC_None;
  325. } else {
  326. return clang::SC_Static;
  327. }
  328. }
  329. // Get the `self` param type, or `None` if the function does not have
  330. // a `self` param.
  331. auto GetSelfTypeId() const -> SemIR::TypeId {
  332. if (self_param) {
  333. return self_param->type_id;
  334. }
  335. return SemIR::TypeId::None;
  336. }
  337. SemIR::FunctionId function_id;
  338. const SemIR::Function& function;
  339. // Parent scope in the C++ AST where a C++ thunk for this function can
  340. // be created. If the function is a method, this will be a
  341. // `CXXRecordDecl`.
  342. clang::DeclContext* decl_context;
  343. // For each of the function's explicit parameters, the scrutinee type
  344. // and whether the parameter is a reference.
  345. llvm::SmallVector<Param> explicit_params;
  346. // For methods, the type of `self` and whether it is a reference. If
  347. // the function does not have a `self` parameter, this is `nullopt`.
  348. std::optional<Param> self_param;
  349. };
  350. } // namespace
  351. // Create a `clang::FunctionDecl` for the given Carbon function. This
  352. // can be used to call the Carbon function from C++. The Carbon
  353. // function's ABI must be compatible with C++.
  354. //
  355. // The resulting decl is used to allow a generated C++ function to call
  356. // a generated Carbon function.
  357. static auto BuildCppFunctionDeclForCarbonFn(Context& context,
  358. SemIR::LocId loc_id,
  359. SemIR::FunctionId function_id)
  360. -> clang::FunctionDecl* {
  361. auto clang_loc = GetCppLocation(context, loc_id);
  362. const SemIR::Function& function = context.functions().Get(function_id);
  363. FunctionInfo callee(context, function_id, function, nullptr);
  364. // Get parameters types.
  365. llvm::SmallVector<clang::QualType> cpp_param_types;
  366. if (callee.self_param) {
  367. auto cpp_type = MapToCppType(context, callee.self_param->type_id);
  368. if (cpp_type.isNull()) {
  369. context.TODO(loc_id, "failed to map Carbon self type to C++");
  370. return nullptr;
  371. }
  372. cpp_type = context.ast_context().getLValueReferenceType(cpp_type);
  373. cpp_param_types.push_back(cpp_type);
  374. }
  375. for (auto param : callee.explicit_params) {
  376. auto cpp_type = MapToCppType(context, param.type_id);
  377. if (cpp_type.isNull()) {
  378. context.TODO(loc_id, "failed to map Carbon type to C++");
  379. return nullptr;
  380. }
  381. auto ref_type = context.ast_context().getLValueReferenceType(cpp_type);
  382. cpp_param_types.push_back(ref_type);
  383. }
  384. CARBON_CHECK(function.return_type_inst_id == SemIR::TypeInstId::None);
  385. auto cpp_return_type = context.ast_context().VoidTy;
  386. auto cpp_function_type = context.ast_context().getFunctionType(
  387. cpp_return_type, cpp_param_types,
  388. clang::FunctionProtoType::ExtProtoInfo());
  389. auto* identifier_info = GetClangIdentifierInfo(context, function.name_id);
  390. CARBON_CHECK(identifier_info, "function with non-identifier name {0}",
  391. function.name_id);
  392. clang::FunctionDecl* function_decl = clang::FunctionDecl::Create(
  393. context.ast_context(), context.ast_context().getTranslationUnitDecl(),
  394. /*StartLoc=*/clang_loc, /*NLoc=*/clang_loc, identifier_info,
  395. cpp_function_type, /*TInfo=*/nullptr, clang::SC_Extern);
  396. // Build parameter decls.
  397. llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
  398. for (auto [i, type] : llvm::enumerate(cpp_param_types)) {
  399. clang::ParmVarDecl* param = clang::ParmVarDecl::Create(
  400. context.ast_context(), function_decl, /*StartLoc=*/clang_loc,
  401. /*IdLoc=*/clang_loc, /*Id=*/nullptr, type, /*TInfo=*/nullptr,
  402. clang::SC_None, /*DefArg=*/nullptr);
  403. param_var_decls.push_back(param);
  404. }
  405. function_decl->setParams(param_var_decls);
  406. // Mangle the function name and attach it to the `FunctionDecl`.
  407. SemIR::Mangler m(context.sem_ir(), context.total_ir_count());
  408. std::string mangled_name = m.Mangle(function_id, SemIR::SpecificId::None);
  409. function_decl->addAttr(
  410. clang::AsmLabelAttr::Create(context.ast_context(), mangled_name));
  411. return function_decl;
  412. }
  413. // Create the declaration of the C++ thunk.
  414. static auto BuildCppToCarbonThunkDecl(
  415. Context& context, SemIR::LocId loc_id, const FunctionInfo& target,
  416. clang::DeclarationName thunk_name,
  417. llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
  418. clang::ASTContext& ast_context = context.ast_context();
  419. auto clang_loc = GetCppLocation(context, loc_id);
  420. // Get the C++ return type (this corresponds to the return type of the
  421. // target Carbon function).
  422. clang::QualType cpp_return_type = context.ast_context().VoidTy;
  423. auto return_type_id = target.function.GetDeclaredReturnType(context.sem_ir());
  424. if (return_type_id != SemIR::TypeId::None) {
  425. cpp_return_type = MapToCppType(context, return_type_id);
  426. if (cpp_return_type.isNull()) {
  427. context.TODO(loc_id, "failed to map Carbon return type to C++ type");
  428. return nullptr;
  429. }
  430. }
  431. clang::DeclarationNameInfo name_info(thunk_name, clang_loc);
  432. auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
  433. if (target.self_param && target.self_param->is_ref) {
  434. ext_proto_info.RefQualifier = clang::RQ_LValue;
  435. }
  436. clang::QualType thunk_function_type = ast_context.getFunctionType(
  437. cpp_return_type, thunk_param_types, ext_proto_info);
  438. auto* tinfo =
  439. ast_context.getTrivialTypeSourceInfo(thunk_function_type, clang_loc);
  440. bool uses_fp_intrin = false;
  441. bool inline_specified = true;
  442. auto constexpr_kind = clang::ConstexprSpecKind::Unspecified;
  443. auto trailing_requires_clause = clang::AssociatedConstraint();
  444. clang::FunctionDecl* thunk_function_decl = nullptr;
  445. if (auto* parent_class =
  446. dyn_cast<clang::CXXRecordDecl>(target.decl_context)) {
  447. thunk_function_decl = clang::CXXMethodDecl::Create(
  448. ast_context, parent_class, clang_loc, name_info, thunk_function_type,
  449. tinfo, target.GetStorageClass(), uses_fp_intrin, inline_specified,
  450. constexpr_kind, clang_loc, trailing_requires_clause);
  451. // TODO: Map Carbon access to C++ access.
  452. thunk_function_decl->setAccess(clang::AS_public);
  453. } else {
  454. thunk_function_decl = clang::FunctionDecl::Create(
  455. ast_context, target.decl_context, clang_loc, name_info,
  456. thunk_function_type, tinfo, clang::SC_None, uses_fp_intrin,
  457. inline_specified,
  458. /*hasWrittenPrototype=*/true, constexpr_kind, trailing_requires_clause);
  459. }
  460. target.decl_context->addHiddenDecl(thunk_function_decl);
  461. llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
  462. for (auto [i, type] : llvm::enumerate(thunk_param_types)) {
  463. clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
  464. ast_context, thunk_function_decl, /*StartLoc=*/clang_loc,
  465. /*IdLoc=*/clang_loc, /*Id=*/nullptr, type,
  466. /*TInfo=*/nullptr, clang::SC_None, /*DefArg=*/nullptr);
  467. param_var_decls.push_back(thunk_param);
  468. }
  469. thunk_function_decl->setParams(param_var_decls);
  470. // Force the thunk to be inlined and discarded.
  471. thunk_function_decl->addAttr(
  472. clang::AlwaysInlineAttr::CreateImplicit(ast_context));
  473. thunk_function_decl->addAttr(
  474. clang::InternalLinkageAttr::CreateImplicit(ast_context));
  475. return thunk_function_decl;
  476. }
  477. // Create the body of a C++ thunk that calls a Carbon thunk. The
  478. // arguments are passed by reference to the callee.
  479. static auto BuildCppToCarbonThunkBody(clang::Sema& sema,
  480. const FunctionInfo& target,
  481. clang::FunctionDecl* function_decl,
  482. clang::FunctionDecl* callee_function_decl)
  483. -> clang::StmtResult {
  484. clang::SourceLocation clang_loc = function_decl->getLocation();
  485. llvm::SmallVector<clang::Stmt*> stmts;
  486. // Create return storage if the target function returns non-void.
  487. const bool has_return_value = !function_decl->getReturnType()->isVoidType();
  488. clang::VarDecl* return_storage_var_decl = nullptr;
  489. clang::ExprResult return_storage_expr;
  490. if (has_return_value) {
  491. auto& return_storage_ident =
  492. sema.getASTContext().Idents.get("return_storage");
  493. return_storage_var_decl =
  494. clang::VarDecl::Create(sema.getASTContext(), function_decl,
  495. /*StartLoc=*/clang_loc,
  496. /*IdLoc=*/clang_loc, &return_storage_ident,
  497. function_decl->getReturnType(),
  498. /*TInfo=*/nullptr, clang::SC_None);
  499. return_storage_var_decl->setNRVOVariable(true);
  500. return_storage_expr = sema.BuildDeclRefExpr(
  501. return_storage_var_decl, return_storage_var_decl->getType(),
  502. clang::VK_LValue, clang_loc);
  503. auto decl_group_ref = clang::DeclGroupRef(return_storage_var_decl);
  504. auto decl_stmt =
  505. sema.ActOnDeclStmt(clang::Sema::DeclGroupPtrTy::make(decl_group_ref),
  506. clang_loc, clang_loc);
  507. stmts.push_back(decl_stmt.get());
  508. }
  509. clang::ExprResult callee = sema.BuildDeclRefExpr(
  510. callee_function_decl, callee_function_decl->getType(), clang::VK_PRValue,
  511. clang_loc);
  512. llvm::SmallVector<clang::Expr*> call_args;
  513. // For methods, pass the `this` pointer as the first argument to the callee.
  514. if (target.self_param) {
  515. auto* parent_class = cast<clang::CXXRecordDecl>(target.decl_context);
  516. clang::QualType class_type =
  517. sema.getASTContext().getCanonicalTagType(parent_class);
  518. auto class_ptr_type = sema.getASTContext().getPointerType(class_type);
  519. auto* this_expr = sema.BuildCXXThisExpr(clang_loc, class_ptr_type,
  520. /*IsImplicit=*/true);
  521. this_expr = clang::UnaryOperator::Create(
  522. sema.getASTContext(), this_expr, clang::UO_Deref, class_type,
  523. clang::ExprValueKind::VK_LValue, clang::ExprObjectKind::OK_Ordinary,
  524. clang_loc, /*CanOverflow=*/false, clang::FPOptionsOverride());
  525. call_args.push_back(this_expr);
  526. }
  527. for (auto* param : function_decl->parameters()) {
  528. clang::Expr* call_arg =
  529. sema.BuildDeclRefExpr(param, param->getType().getNonReferenceType(),
  530. clang::VK_LValue, clang_loc);
  531. call_args.push_back(call_arg);
  532. }
  533. // If the target function returns non-void, the Carbon thunk takes an
  534. // extra output parameter referencing the return storage.
  535. if (has_return_value) {
  536. call_args.push_back(return_storage_expr.get());
  537. }
  538. clang::ExprResult call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc,
  539. call_args, clang_loc);
  540. CARBON_CHECK(call.isUsable());
  541. stmts.push_back(call.get());
  542. if (has_return_value) {
  543. auto* return_stmt = clang::ReturnStmt::Create(
  544. sema.getASTContext(), clang_loc, return_storage_expr.get(),
  545. return_storage_var_decl);
  546. stmts.push_back(return_stmt);
  547. }
  548. return clang::CompoundStmt::Create(sema.getASTContext(), stmts,
  549. clang::FPOptionsOverride(), clang_loc,
  550. clang_loc);
  551. }
  552. // Create a C++ thunk that calls the Carbon thunk. The C++ thunk's
  553. // parameter types are mapped from the parameters of the target function
  554. // with `MapToCppType`. (Note that the target function here is the
  555. // callee of the Carbon thunk.)
  556. static auto BuildCppToCarbonThunk(Context& context, SemIR::LocId loc_id,
  557. const FunctionInfo& target,
  558. llvm::StringRef thunk_name,
  559. clang::FunctionDecl* carbon_function_decl)
  560. -> clang::FunctionDecl* {
  561. auto& thunk_ident = context.ast_context().Idents.get(thunk_name);
  562. llvm::SmallVector<clang::QualType> param_types;
  563. for (auto param : target.explicit_params) {
  564. auto cpp_type = MapToCppType(context, param.type_id);
  565. if (cpp_type.isNull()) {
  566. context.TODO(loc_id, "failed to map C++ type to Carbon");
  567. return nullptr;
  568. }
  569. if (param.is_ref) {
  570. cpp_type = context.ast_context().getLValueReferenceType(cpp_type);
  571. }
  572. param_types.push_back(cpp_type);
  573. }
  574. auto* thunk_function_decl = BuildCppToCarbonThunkDecl(
  575. context, loc_id, target, &thunk_ident, param_types);
  576. // Build the thunk function body.
  577. clang::Sema& sema = context.clang_sema();
  578. clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
  579. sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
  580. clang::StmtResult body = BuildCppToCarbonThunkBody(
  581. sema, target, thunk_function_decl, carbon_function_decl);
  582. sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
  583. CARBON_CHECK(!body.isInvalid());
  584. context.clang_sema().getASTConsumer().HandleTopLevelDecl(
  585. clang::DeclGroupRef(thunk_function_decl));
  586. return thunk_function_decl;
  587. }
  588. // Create a Carbon thunk that calls `callee`. The thunk's parameters are
  589. // all references to the callee parameter type.
  590. static auto BuildCarbonToCarbonThunk(Context& context, SemIR::LocId loc_id,
  591. const FunctionInfo& target)
  592. -> SemIR::FunctionId {
  593. // Create the thunk's name.
  594. llvm::SmallString<64> thunk_name =
  595. context.names().GetFormatted(target.function.name_id);
  596. thunk_name += "__carbon_thunk";
  597. auto& ident = context.ast_context().Idents.get(thunk_name);
  598. auto thunk_name_id =
  599. SemIR::NameId::ForIdentifier(context.identifiers().Add(ident.getName()));
  600. // Get the thunk's parameters. These match the callee parameters, with
  601. // the addition of an output parameter for the callee's return value
  602. // (if it has one).
  603. llvm::SmallVector<SemIR::TypeId> thunk_param_type_ids;
  604. for (const auto& param : target.explicit_params) {
  605. thunk_param_type_ids.push_back(param.type_id);
  606. }
  607. auto callee_return_type_id =
  608. target.function.GetDeclaredReturnType(context.sem_ir());
  609. if (callee_return_type_id != SemIR::TypeId::None) {
  610. thunk_param_type_ids.push_back(callee_return_type_id);
  611. }
  612. auto carbon_thunk_function_id =
  613. MakeGeneratedFunctionDecl(
  614. context, loc_id,
  615. {.parent_scope_id = target.function.parent_scope_id,
  616. .name_id = thunk_name_id,
  617. .self_type_id = target.GetSelfTypeId(),
  618. .param_type_ids = thunk_param_type_ids,
  619. .param_kind = ParamPatternKind::Ref})
  620. .second;
  621. BuildThunkDefinitionForExport(
  622. context, carbon_thunk_function_id, target.function_id,
  623. context.functions().Get(carbon_thunk_function_id).first_decl_id(),
  624. target.function.first_decl_id());
  625. return carbon_thunk_function_id;
  626. }
  627. auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
  628. SemIR::FunctionId callee_function_id)
  629. -> clang::FunctionDecl* {
  630. const SemIR::Function& callee = context.functions().Get(callee_function_id);
  631. if (callee.generic_id.has_value()) {
  632. context.TODO(loc_id,
  633. "unsupported: C++ calling a Carbon function with "
  634. "generic parameters");
  635. return nullptr;
  636. }
  637. // Map the parent scope into the C++ AST.
  638. auto* decl_context =
  639. ExportNameScopeToCpp(context, loc_id, callee.parent_scope_id);
  640. if (!decl_context) {
  641. return nullptr;
  642. }
  643. FunctionInfo target_function_info(context, callee_function_id, callee,
  644. decl_context);
  645. // Create a Carbon thunk that calls the callee. The thunk's parameters
  646. // are all references so that the ABI is compatible with C++ callers.
  647. auto carbon_thunk_function_id =
  648. BuildCarbonToCarbonThunk(context, loc_id, target_function_info);
  649. // Create a `clang::FunctionDecl` that can be used to call the Carbon thunk.
  650. auto* carbon_function_decl = BuildCppFunctionDeclForCarbonFn(
  651. context, loc_id, carbon_thunk_function_id);
  652. if (!carbon_function_decl) {
  653. return nullptr;
  654. }
  655. // Create a C++ thunk that calls the Carbon thunk.
  656. return BuildCppToCarbonThunk(context, loc_id, target_function_info,
  657. context.names().GetFormatted(callee.name_id),
  658. carbon_function_decl);
  659. }
  660. } // namespace Carbon::Check