export.cpp 12 KB

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