call.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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/call.h"
  5. #include "clang/Sema/Sema.h"
  6. #include "clang/Sema/Template.h"
  7. #include "toolchain/base/kind_switch.h"
  8. #include "toolchain/check/call.h"
  9. #include "toolchain/check/cpp/import.h"
  10. #include "toolchain/check/cpp/location.h"
  11. #include "toolchain/check/cpp/operators.h"
  12. #include "toolchain/check/cpp/overload_resolution.h"
  13. #include "toolchain/check/cpp/type_mapping.h"
  14. #include "toolchain/check/literal.h"
  15. #include "toolchain/sem_ir/function.h"
  16. #include "toolchain/sem_ir/ids.h"
  17. #include "toolchain/sem_ir/typed_insts.h"
  18. namespace Carbon::Check {
  19. // Returns true if the given instruction can only be a template argument, and
  20. // not a function argument. We classify arguments as definitely being template
  21. // arguments if they are types or the name of a template or generic.
  22. // TODO: We should also have a way to specify that an argument is a non-type
  23. // template argument.
  24. static auto IsTemplateArg(Context& context, SemIR::InstId arg_id) -> bool {
  25. auto arg_type_id = context.insts().Get(arg_id).type_id();
  26. auto arg_type = context.types().GetAsInst(arg_type_id);
  27. return arg_type
  28. .IsOneOf<SemIR::TypeType, SemIR::FacetType, SemIR::CppTemplateNameType,
  29. SemIR::GenericClassType, SemIR::GenericInterfaceType,
  30. SemIR::GenericNamedConstraintType>();
  31. }
  32. // Splits a call argument list into a list of template arguments followed by a
  33. // list of function arguments. We split the argument list as early as possible,
  34. // subject to the constraint that if an argument is a template argument, it goes
  35. // in the template argument list.
  36. static auto SplitCallArgumentList(Context& context,
  37. llvm::ArrayRef<SemIR::InstId> arg_ids)
  38. -> std::pair<llvm::ArrayRef<SemIR::InstId>, llvm::ArrayRef<SemIR::InstId>> {
  39. for (auto [n, arg_id] : llvm::enumerate(llvm::reverse(arg_ids))) {
  40. if (IsTemplateArg(context, arg_id)) {
  41. return {arg_ids.drop_back(n), arg_ids.take_back(n)};
  42. }
  43. }
  44. // No template arguments found.
  45. return {{}, arg_ids};
  46. }
  47. auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id,
  48. SemIR::CppOverloadSetId overload_set_id,
  49. SemIR::InstId self_id,
  50. llvm::ArrayRef<SemIR::InstId> arg_ids,
  51. bool is_operator_syntax) -> SemIR::InstId {
  52. auto [template_arg_ids, function_arg_ids] =
  53. SplitCallArgumentList(context, arg_ids);
  54. auto callee_id = PerformCppOverloadResolution(
  55. context, loc_id, context.cpp_overload_sets().Get(overload_set_id),
  56. template_arg_ids, self_id, function_arg_ids);
  57. SemIR::Callee callee = GetCallee(context.sem_ir(), callee_id);
  58. CARBON_KIND_SWITCH(callee) {
  59. case CARBON_KIND(SemIR::CalleeError _): {
  60. return SemIR::ErrorInst::InstId;
  61. }
  62. case CARBON_KIND(SemIR::CalleeFunction fn): {
  63. CARBON_CHECK(!fn.self_id.has_value());
  64. if (self_id.has_value()) {
  65. // Preserve the `self` argument from the original callee.
  66. fn.self_id = self_id;
  67. }
  68. return PerformCallToFunction(context, loc_id, callee_id, fn,
  69. function_arg_ids, is_operator_syntax);
  70. }
  71. case CARBON_KIND(SemIR::CalleeCppOverloadSet _): {
  72. CARBON_FATAL("overloads can't be recursive");
  73. }
  74. case CARBON_KIND(SemIR::CalleeNonFunction _): {
  75. CARBON_FATAL("overloads should produce functions");
  76. }
  77. }
  78. }
  79. // Synthesize a placeholder `void{}` template argument, that will never be a
  80. // valid argument for any template parameter. This is used in order to get Clang
  81. // to diagnose invalid template argument errors for us. The location of the
  82. // Carbon expression is used as the location of the C++ expression, so
  83. // Clang's diagnostics will point into the Carbon code.
  84. //
  85. // TODO: If Clang ever tries to print the type of the expression or to
  86. // pretty-print the expression itself, it would print the wrong thing. Currently
  87. // this doesn't appear to happen, but in principle it could. Ideally we'd add an
  88. // extension point to Clang to represent a "foreign expression" and use it here
  89. // instead of creating a bogus placeholder expression.
  90. static auto MakePlaceholderTemplateArg(Context& context, SemIR::InstId arg_id)
  91. -> clang::TemplateArgumentLoc {
  92. auto arg_loc = GetCppLocation(context, SemIR::LocId(arg_id));
  93. auto void_type = context.ast_context().VoidTy;
  94. auto* arg = new (context.ast_context()) clang::CXXScalarValueInitExpr(
  95. void_type,
  96. context.ast_context().getTrivialTypeSourceInfo(void_type, arg_loc),
  97. arg_loc);
  98. return clang::TemplateArgumentLoc(
  99. clang::TemplateArgument(arg, /*IsCanonical=*/false), arg);
  100. }
  101. // Converts an argument in a call to a C++ template name into a corresponding
  102. // clang template argument, given the template parameter it will be matched
  103. // against.
  104. static auto ConvertArgToTemplateArg(
  105. Context& context, clang::TemplateDecl* template_decl,
  106. clang::NamedDecl* param_decl, SemIR::InstId arg_id,
  107. clang::SmallVector<clang::TemplateArgument>* template_args,
  108. unsigned argument_pack_index, bool diagnose)
  109. -> std::optional<clang::TemplateArgumentLoc> {
  110. if (isa<clang::TemplateTypeParmDecl>(param_decl)) {
  111. auto type = ExprAsType(context, SemIR::LocId(arg_id), arg_id, diagnose);
  112. if (type.type_id == SemIR::ErrorInst::TypeId) {
  113. return std::nullopt;
  114. }
  115. auto clang_type = MapToCppType(context, type.type_id);
  116. if (clang_type.isNull()) {
  117. if (diagnose) {
  118. context.TODO(arg_id, "unsupported type used as template argument");
  119. }
  120. return std::nullopt;
  121. }
  122. return clang::TemplateArgumentLoc(
  123. clang_type,
  124. context.ast_context().getTrivialTypeSourceInfo(
  125. clang_type, GetCppLocation(context, SemIR::LocId(arg_id))));
  126. }
  127. if (isa<clang::TemplateTemplateParmDecl>(param_decl)) {
  128. auto inst = context.sem_ir().insts().Get(arg_id);
  129. if (auto template_name_type =
  130. context.types().TryGetAs<SemIR::CppTemplateNameType>(
  131. inst.type_id())) {
  132. clang::TemplateName name(cast<clang::TemplateDecl>(
  133. context.clang_decls().Get(template_name_type->decl_id).key.decl));
  134. return clang::TemplateArgumentLoc(
  135. context.ast_context(), clang::TemplateArgument(name),
  136. /*TemplateKWLoc=*/clang::SourceLocation(),
  137. clang::NestedNameSpecifierLoc(),
  138. GetCppLocation(context, SemIR::LocId(arg_id)));
  139. }
  140. // TODO: Eventually we should also support passing Carbon generics as
  141. // template template arguments.
  142. return MakePlaceholderTemplateArg(context, arg_id);
  143. }
  144. if (auto* non_type = dyn_cast<clang::NonTypeTemplateParmDecl>(param_decl)) {
  145. auto param_type = non_type->getType();
  146. if (non_type->isParameterPack() && non_type->isExpandedParameterPack()) {
  147. param_type = non_type->getExpansionType(argument_pack_index);
  148. }
  149. // Handle non-type parameters with a dependent type. For example:
  150. //
  151. // C++: template<typename T, T N> struct S{};
  152. // Carbon: Cpp.S(i32, 42)
  153. //
  154. // When evaluating the second template argument, the generic type of
  155. // `T` should be substituted with `i32`.
  156. if (param_type->isInstantiationDependentType()) {
  157. // If we don't want to diagnose errors, create a SFINAE context so that
  158. // Clang knows to suppress error messages.
  159. std::optional<clang::Sema::SFINAETrap> sfinae;
  160. if (!diagnose) {
  161. sfinae.emplace(context.clang_sema());
  162. }
  163. clang::Sema::InstantiatingTemplate inst(
  164. context.clang_sema(), clang::SourceLocation(), param_decl, non_type,
  165. *template_args, clang::SourceRange());
  166. if (inst.isInvalid()) {
  167. return std::nullopt;
  168. }
  169. clang::MultiLevelTemplateArgumentList mltal(template_decl, *template_args,
  170. /*Final=*/true);
  171. mltal.addOuterRetainedLevels(non_type->getDepth());
  172. if (const auto* pet = param_type->getAs<clang::PackExpansionType>()) {
  173. clang::Sema::ArgPackSubstIndexRAII subst_index(context.clang_sema(),
  174. argument_pack_index);
  175. param_type = context.clang_sema().SubstType(pet->getPattern(), mltal,
  176. non_type->getLocation(),
  177. non_type->getDeclName());
  178. } else {
  179. param_type = context.clang_sema().SubstType(param_type, mltal,
  180. non_type->getLocation(),
  181. non_type->getDeclName());
  182. }
  183. if (!param_type.isNull()) {
  184. param_type = context.clang_sema().CheckNonTypeTemplateParameterType(
  185. param_type, non_type->getLocation());
  186. }
  187. if (param_type.isNull() || (sfinae && sfinae->hasErrorOccurred())) {
  188. return std::nullopt;
  189. }
  190. }
  191. // Get the Carbon type corresponding to the parameter's Clang type.
  192. const auto type_expr =
  193. ImportCppType(context, SemIR::LocId(arg_id), param_type);
  194. // Try to convert the argument to the parameter type.
  195. const auto converted_inst_id =
  196. Convert(context, SemIR::LocId(arg_id), arg_id,
  197. {
  198. .kind = ConversionTarget::Value,
  199. .type_id = type_expr.type_id,
  200. .diagnose = diagnose,
  201. });
  202. if (converted_inst_id == SemIR::ErrorInst::InstId) {
  203. return std::nullopt;
  204. }
  205. // TODO: provide a better location.
  206. auto template_loc = clang::TemplateArgumentLocInfo();
  207. auto const_inst_id =
  208. context.constant_values().GetConstantInstId(converted_inst_id);
  209. if (const_inst_id.has_value()) {
  210. if (param_type->isIntegerType()) {
  211. const bool is_signed = param_type->isSignedIntegerOrEnumerationType();
  212. if (auto int_value =
  213. context.insts().TryGetAs<SemIR::IntValue>(const_inst_id)) {
  214. const auto& ap_int = context.ints().Get(int_value->int_id);
  215. auto aps_int =
  216. llvm::APSInt(ap_int, !is_signed)
  217. .extOrTrunc(context.ast_context().getIntWidth(param_type));
  218. clang::TemplateArgument template_arg(context.ast_context(), aps_int,
  219. param_type);
  220. return clang::TemplateArgumentLoc(template_arg, template_loc);
  221. } else if (auto bool_value =
  222. context.insts().TryGetAs<SemIR::BoolLiteral>(
  223. const_inst_id)) {
  224. llvm::APInt ap_int(context.ast_context().getIntWidth(param_type),
  225. bool_value->value.ToBool(), is_signed);
  226. auto aps_int =
  227. llvm::APSInt(ap_int, !is_signed)
  228. .extOrTrunc(context.ast_context().getIntWidth(param_type));
  229. auto template_arg = clang::TemplateArgument(context.ast_context(),
  230. aps_int, param_type);
  231. return clang::TemplateArgumentLoc(template_arg, template_loc);
  232. }
  233. } else if (param_type->isFloatingType()) {
  234. if (auto float_value =
  235. context.insts().TryGetAs<SemIR::FloatValue>(const_inst_id)) {
  236. const auto& ap_float = context.floats().Get(float_value->float_id);
  237. clang::TemplateArgument template_arg(
  238. context.ast_context(), param_type, clang::APValue(ap_float));
  239. return clang::TemplateArgumentLoc(template_arg, template_loc);
  240. }
  241. } else if (param_type->isPointerType()) {
  242. if (auto addr_of =
  243. context.insts().TryGetAs<SemIR::AddrOf>(const_inst_id)) {
  244. if (auto* var_decl = GetAsClangVarDecl(context, addr_of->lvalue_id)) {
  245. clang::TemplateArgument template_arg(var_decl, param_type);
  246. return clang::TemplateArgumentLoc(template_arg, template_loc);
  247. }
  248. // TODO: support pointers to variables declared in Carbon.
  249. }
  250. }
  251. }
  252. // TODO: Support other types.
  253. if (diagnose) {
  254. context.TODO(arg_id,
  255. "unsupported argument type for non-type template parameter");
  256. }
  257. return std::nullopt;
  258. }
  259. CARBON_FATAL("Unknown declaration kind for template parameter");
  260. }
  261. auto ConvertArgsToTemplateArgs(Context& context,
  262. clang::TemplateDecl* template_decl,
  263. llvm::ArrayRef<SemIR::InstId> arg_ids,
  264. clang::TemplateArgumentListInfo& arg_list,
  265. bool diagnose) -> bool {
  266. clang::SmallVector<clang::TemplateArgument> template_args;
  267. for (auto* param_decl : template_decl->getTemplateParameters()->asArray()) {
  268. if (arg_ids.empty()) {
  269. return true;
  270. }
  271. // A parameter pack consumes all remaining arguments; otherwise, it consumes
  272. // a single argument.
  273. // TODO: Handle expanded template parameter packs, which have a known, fixed
  274. // arity.
  275. llvm::ArrayRef<SemIR::InstId> args_for_param =
  276. param_decl->isTemplateParameterPack() ? std::exchange(arg_ids, {})
  277. : arg_ids.consume_front();
  278. for (auto [argument_pack_index, arg_id] : llvm::enumerate(args_for_param)) {
  279. if (auto arg = ConvertArgToTemplateArg(context, template_decl, param_decl,
  280. arg_id, &template_args,
  281. argument_pack_index, diagnose)) {
  282. arg_list.addArgument(*arg);
  283. template_args.push_back(arg->getArgument());
  284. argument_pack_index++;
  285. } else {
  286. return false;
  287. }
  288. }
  289. }
  290. // If there are any remaining arguments, that's an error; convert them to
  291. // placeholder template arguments so that Clang will diagnose it for us.
  292. for (auto arg_id : arg_ids) {
  293. // Synthesize a placeholder `void{}` template argument.
  294. arg_list.addArgument(MakePlaceholderTemplateArg(context, arg_id));
  295. }
  296. return true;
  297. }
  298. // Given a template and an template argument list, builds a Carbon value
  299. // describing the corresponding C++ template-id.
  300. static auto BuildTemplateId(Context& context, SemIR::LocId loc_id,
  301. clang::SourceLocation loc,
  302. clang::TemplateDecl* template_decl,
  303. clang::TemplateArgumentListInfo& arg_list)
  304. -> SemIR::InstId {
  305. if (auto* var_template_decl =
  306. dyn_cast<clang::VarTemplateDecl>(template_decl)) {
  307. auto decl_result = context.clang_sema().CheckVarTemplateId(
  308. var_template_decl, /*TemplateLoc=*/clang::SourceLocation(), loc,
  309. arg_list, /*SetWrittenArgs=*/false);
  310. return decl_result.isInvalid()
  311. ? SemIR::ErrorInst::InstId
  312. : ImportCppDecl(context, loc_id,
  313. SemIR::ClangDeclKey::ForNonFunctionDecl(
  314. decl_result.get()));
  315. }
  316. if (auto* concept_decl = dyn_cast<clang::ConceptDecl>(template_decl)) {
  317. auto expr_result = context.clang_sema().CheckConceptTemplateId(
  318. clang::CXXScopeSpec(), /*TemplateKWLoc=*/clang::SourceLocation(),
  319. clang::DeclarationNameInfo(concept_decl->getDeclName(), loc),
  320. concept_decl, concept_decl, &arg_list);
  321. if (expr_result.isInvalid()) {
  322. return SemIR::ErrorInst::InstId;
  323. }
  324. auto* expr = expr_result.getAs<clang::ConceptSpecializationExpr>();
  325. return MakeBoolLiteral(context, loc_id,
  326. SemIR::BoolValue::From(expr->isSatisfied()));
  327. }
  328. clang::TemplateName template_name(template_decl);
  329. auto clang_type = context.clang_sema().CheckTemplateIdType(
  330. clang::ElaboratedTypeKeyword::None, template_name, loc, arg_list,
  331. /*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false);
  332. if (clang_type.isNull()) {
  333. return SemIR::ErrorInst::InstId;
  334. }
  335. return ImportCppType(context, loc_id, clang_type).inst_id;
  336. }
  337. auto PerformCallToCppTemplateName(Context& context, SemIR::LocId loc_id,
  338. SemIR::ClangDeclId template_decl_id,
  339. llvm::ArrayRef<SemIR::InstId> arg_ids)
  340. -> SemIR::InstId {
  341. auto* template_decl = dyn_cast<clang::TemplateDecl>(
  342. context.clang_decls().Get(template_decl_id).key.decl);
  343. auto loc = GetCppLocation(context, loc_id);
  344. // Form a template argument list for this template.
  345. clang::TemplateArgumentListInfo arg_list(loc, loc);
  346. if (!ConvertArgsToTemplateArgs(context, template_decl, arg_ids, arg_list)) {
  347. return SemIR::ErrorInst::InstId;
  348. }
  349. return BuildTemplateId(context, loc_id, loc, template_decl, arg_list);
  350. }
  351. } // namespace Carbon::Check