export.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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. // Create a `clang::FunctionDecl` for the given Carbon function. This
  14. // can be used to call the Carbon function from C++. The Carbon
  15. // function's ABI must be compatible with C++.
  16. //
  17. // The resulting decl is used to allow a generated C++ function to call
  18. // a generated Carbon function.
  19. static auto BuildCppFunctionDeclForCarbonFn(Context& context,
  20. clang::DeclContext& decl_context,
  21. SemIR::LocId loc_id,
  22. SemIR::FunctionId function_id)
  23. -> clang::FunctionDecl* {
  24. auto clang_loc = GetCppLocation(context, loc_id);
  25. const SemIR::Function& function = context.functions().Get(function_id);
  26. // Get parameters types.
  27. auto carbon_function_params =
  28. context.inst_blocks().Get(function.call_param_patterns_id);
  29. llvm::SmallVector<clang::QualType> cpp_param_types;
  30. for (auto param_inst_id : carbon_function_params) {
  31. auto scrutinee_type_id = ExtractScrutineeType(
  32. context.sem_ir(), context.insts().Get(param_inst_id).type_id());
  33. auto cpp_type = MapToCppType(context, scrutinee_type_id);
  34. if (cpp_type.isNull()) {
  35. context.TODO(loc_id, "failed to map C++ type to Carbon");
  36. return nullptr;
  37. }
  38. auto ref_type = context.ast_context().getLValueReferenceType(cpp_type);
  39. cpp_param_types.push_back(ref_type);
  40. }
  41. CARBON_CHECK(function.return_type_inst_id == SemIR::TypeInstId::None);
  42. auto cpp_return_type = context.ast_context().VoidTy;
  43. auto cpp_function_type = context.ast_context().getFunctionType(
  44. cpp_return_type, cpp_param_types,
  45. clang::FunctionProtoType::ExtProtoInfo());
  46. auto* identifier_info = GetClangIdentifierInfo(context, function.name_id);
  47. CARBON_CHECK(identifier_info, "function with non-identifier name {0}",
  48. function.name_id);
  49. auto* function_decl = clang::FunctionDecl::Create(
  50. context.ast_context(), &decl_context,
  51. /*StartLoc=*/clang_loc,
  52. /*NLoc=*/clang_loc, clang::DeclarationName(identifier_info),
  53. cpp_function_type,
  54. /*TInfo=*/nullptr, clang::SC_Extern);
  55. // Build parameter decls.
  56. llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
  57. for (auto [i, type] : llvm::enumerate(cpp_param_types)) {
  58. clang::ParmVarDecl* param = clang::ParmVarDecl::Create(
  59. context.ast_context(), function_decl, /*StartLoc=*/clang_loc,
  60. /*IdLoc=*/clang_loc, /*Id=*/nullptr, type, /*TInfo=*/nullptr,
  61. clang::SC_None, /*DefArg=*/nullptr);
  62. param_var_decls.push_back(param);
  63. }
  64. function_decl->setParams(param_var_decls);
  65. // Mangle the function name and attach it to the `FunctionDecl`.
  66. SemIR::Mangler m(context.sem_ir(), context.total_ir_count());
  67. std::string mangled_name = m.Mangle(function_id, SemIR::SpecificId::None);
  68. function_decl->addAttr(
  69. clang::AsmLabelAttr::Create(context.ast_context(), mangled_name));
  70. return function_decl;
  71. }
  72. // Create the declaration of the C++ thunk.
  73. static auto BuildCppToCarbonThunkDecl(
  74. Context& context, SemIR::LocId loc_id, clang::DeclarationName thunk_name,
  75. llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
  76. clang::ASTContext& ast_context = context.ast_context();
  77. auto clang_loc = GetCppLocation(context, loc_id);
  78. auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
  79. clang::QualType thunk_function_type = ast_context.getFunctionType(
  80. ast_context.VoidTy, thunk_param_types, ext_proto_info);
  81. clang::DeclContext* decl_context = ast_context.getTranslationUnitDecl();
  82. auto* tinfo =
  83. ast_context.getTrivialTypeSourceInfo(thunk_function_type, clang_loc);
  84. clang::FunctionDecl* thunk_function_decl = clang::FunctionDecl::Create(
  85. ast_context, decl_context, clang_loc, clang_loc, thunk_name,
  86. thunk_function_type, tinfo, clang::SC_Static);
  87. decl_context->addDecl(thunk_function_decl);
  88. llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
  89. for (auto [i, type] : llvm::enumerate(thunk_param_types)) {
  90. clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
  91. ast_context, thunk_function_decl, /*StartLoc=*/clang_loc,
  92. /*IdLoc=*/clang_loc, /*Id=*/nullptr, type,
  93. /*TInfo=*/nullptr, clang::SC_None, /*DefArg=*/nullptr);
  94. param_var_decls.push_back(thunk_param);
  95. }
  96. thunk_function_decl->setParams(param_var_decls);
  97. // Set always_inline.
  98. thunk_function_decl->addAttr(
  99. clang::AlwaysInlineAttr::CreateImplicit(ast_context));
  100. return thunk_function_decl;
  101. }
  102. // Create the body of a C++ thunk that calls a Carbon thunk. The
  103. // arguments are passed by reference to the callee.
  104. static auto BuildCppToCarbonThunkBody(clang::Sema& sema,
  105. clang::FunctionDecl* function_decl,
  106. clang::FunctionDecl* callee_function_decl)
  107. -> clang::StmtResult {
  108. clang::SourceLocation clang_loc = function_decl->getLocation();
  109. clang::ExprResult callee = sema.BuildDeclRefExpr(
  110. callee_function_decl, callee_function_decl->getType(), clang::VK_PRValue,
  111. clang_loc);
  112. llvm::SmallVector<clang::Expr*> call_args;
  113. for (auto* param : function_decl->parameters()) {
  114. clang::Expr* call_arg =
  115. sema.BuildDeclRefExpr(param, param->getType().getNonReferenceType(),
  116. clang::VK_LValue, clang_loc);
  117. call_args.push_back(call_arg);
  118. }
  119. clang::ExprResult call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc,
  120. call_args, clang_loc);
  121. CARBON_CHECK(call.isUsable());
  122. return call.get();
  123. }
  124. // Create a C++ thunk that calls the Carbon thunk. The C++ thunk's
  125. // parameter types are mapped from the parameters of the target function
  126. // with `MapToCppType`. (Note that the target function here is the
  127. // callee of the Carbon thunk.)
  128. static auto BuildCppToCarbonThunk(
  129. Context& context, SemIR::LocId loc_id, llvm::StringRef base_name,
  130. clang::FunctionDecl* carbon_function_decl,
  131. llvm::ArrayRef<SemIR::TypeId> callee_param_type_ids)
  132. -> clang::FunctionDecl* {
  133. // Create the thunk's name.
  134. llvm::SmallString<64> thunk_name = base_name;
  135. thunk_name += "__cpp_thunk";
  136. auto& thunk_ident = context.ast_context().Idents.get(thunk_name);
  137. llvm::SmallVector<clang::QualType> param_types;
  138. for (auto type_id : callee_param_type_ids) {
  139. auto cpp_type = MapToCppType(context, type_id);
  140. if (cpp_type.isNull()) {
  141. context.TODO(loc_id, "failed to map C++ type to Carbon");
  142. return nullptr;
  143. }
  144. param_types.push_back(cpp_type);
  145. }
  146. auto* thunk_function_decl =
  147. BuildCppToCarbonThunkDecl(context, loc_id, &thunk_ident, param_types);
  148. // Build the thunk function body.
  149. clang::Sema& sema = context.clang_sema();
  150. clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
  151. sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
  152. clang::StmtResult body = BuildCppToCarbonThunkBody(sema, thunk_function_decl,
  153. carbon_function_decl);
  154. sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
  155. CARBON_CHECK(!body.isInvalid());
  156. context.clang_sema().getASTConsumer().HandleTopLevelDecl(
  157. clang::DeclGroupRef(thunk_function_decl));
  158. return thunk_function_decl;
  159. }
  160. // Create a Carbon thunk that calls `callee`. The thunk's parameters are
  161. // all references to the callee parameter type.
  162. static auto BuildCarbonToCarbonThunk(
  163. Context& context, SemIR::LocId loc_id, const SemIR::Function& callee,
  164. llvm::ArrayRef<SemIR::TypeId> callee_param_type_ids) -> SemIR::FunctionId {
  165. // Create the thunk's name.
  166. llvm::SmallString<64> thunk_name =
  167. context.names().GetFormatted(callee.name_id);
  168. thunk_name += "__carbon_thunk";
  169. auto& ident = context.ast_context().Idents.get(thunk_name);
  170. auto thunk_name_id =
  171. SemIR::NameId::ForIdentifier(context.identifiers().Add(ident.getName()));
  172. auto carbon_thunk_function_id =
  173. MakeGeneratedFunctionDecl(context, loc_id,
  174. {.parent_scope_id = callee.parent_scope_id,
  175. .name_id = thunk_name_id,
  176. .param_type_ids = callee_param_type_ids,
  177. .params_are_refs = true})
  178. .second;
  179. BuildThunkDefinition(context, carbon_thunk_function_id,
  180. carbon_thunk_function_id, callee.first_decl_id(),
  181. callee.first_decl_id());
  182. return carbon_thunk_function_id;
  183. }
  184. auto GetReverseInteropFunctionDecl(Context& context, SemIR::LocId loc_id,
  185. clang::DeclContext& decl_context,
  186. SemIR::FunctionId callee_function_id)
  187. -> clang::FunctionDecl* {
  188. const SemIR::Function& callee = context.functions().Get(callee_function_id);
  189. if (callee.return_type_inst_id != SemIR::TypeInstId::None) {
  190. context.TODO(loc_id,
  191. "unsupported: C++ calling a Carbon function with "
  192. "return type other than `()`");
  193. return nullptr;
  194. }
  195. if (callee.generic_id.has_value()) {
  196. context.TODO(loc_id,
  197. "unsupported: C++ calling a Carbon function with "
  198. "generic parameters");
  199. return nullptr;
  200. }
  201. if (callee.call_param_ranges.implicit_size() != 0) {
  202. context.TODO(loc_id,
  203. "unsupported: C++ calling a Carbon function with "
  204. "an implicit parameter");
  205. return nullptr;
  206. }
  207. // Get the parameter types of the Carbon function being called.
  208. auto callee_function_params =
  209. context.inst_blocks().Get(callee.call_param_patterns_id);
  210. llvm::SmallVector<SemIR::TypeId> callee_param_type_ids;
  211. for (auto callee_param_inst_id : callee_function_params) {
  212. auto scrutinee_type_id = ExtractScrutineeType(
  213. context.sem_ir(), context.insts().Get(callee_param_inst_id).type_id());
  214. callee_param_type_ids.push_back(scrutinee_type_id);
  215. }
  216. // Create a Carbon thunk that calls the callee. The thunk's parameters
  217. // are all references so that the ABI is compatible with C++ callers.
  218. auto carbon_thunk_function_id =
  219. BuildCarbonToCarbonThunk(context, loc_id, callee, callee_param_type_ids);
  220. // Create a `clang::FunctionDecl` that can be used to call the Carbon thunk.
  221. auto* carbon_function_decl = BuildCppFunctionDeclForCarbonFn(
  222. context, decl_context, loc_id, carbon_thunk_function_id);
  223. if (!carbon_function_decl) {
  224. return nullptr;
  225. }
  226. // Create a C++ thunk that calls the Carbon thunk.
  227. return BuildCppToCarbonThunk(context, loc_id,
  228. context.names().GetFormatted(callee.name_id),
  229. carbon_function_decl, callee_param_type_ids);
  230. }
  231. } // namespace Carbon::Check