operators.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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/operators.h"
  5. #include "clang/Sema/Overload.h"
  6. #include "clang/Sema/Sema.h"
  7. #include "toolchain/check/cpp/import.h"
  8. #include "toolchain/check/cpp/location.h"
  9. #include "toolchain/check/cpp/overload_resolution.h"
  10. #include "toolchain/check/cpp/type_mapping.h"
  11. #include "toolchain/check/inst.h"
  12. #include "toolchain/check/type.h"
  13. #include "toolchain/check/type_completion.h"
  14. #include "toolchain/sem_ir/ids.h"
  15. #include "toolchain/sem_ir/typed_insts.h"
  16. namespace Carbon::Check {
  17. // Maps Carbon operator interface and operator names to Clang operator kinds.
  18. static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id,
  19. llvm::StringLiteral interface_name,
  20. llvm::StringLiteral op_name)
  21. -> std::optional<clang::OverloadedOperatorKind> {
  22. // Unary operators.
  23. if (interface_name == "Destroy" || interface_name == "As" ||
  24. interface_name == "ImplicitAs" || interface_name == "Copy") {
  25. // TODO: Support destructors and conversions.
  26. return std::nullopt;
  27. }
  28. // Increment and Decrement.
  29. if (interface_name == "Inc") {
  30. CARBON_CHECK(op_name == "Op");
  31. return clang::OO_PlusPlus;
  32. }
  33. if (interface_name == "Dec") {
  34. CARBON_CHECK(op_name == "Op");
  35. return clang::OO_MinusMinus;
  36. }
  37. // Arithmetic.
  38. if (interface_name == "Negate") {
  39. CARBON_CHECK(op_name == "Op");
  40. return clang::OO_Minus;
  41. }
  42. // Binary operators.
  43. // Arithmetic Operators.
  44. if (interface_name == "AddWith") {
  45. CARBON_CHECK(op_name == "Op");
  46. return clang::OO_Plus;
  47. }
  48. if (interface_name == "SubWith") {
  49. CARBON_CHECK(op_name == "Op");
  50. return clang::OO_Minus;
  51. }
  52. if (interface_name == "MulWith") {
  53. CARBON_CHECK(op_name == "Op");
  54. return clang::OO_Star;
  55. }
  56. if (interface_name == "DivWith") {
  57. CARBON_CHECK(op_name == "Op");
  58. return clang::OO_Slash;
  59. }
  60. if (interface_name == "ModWith") {
  61. CARBON_CHECK(op_name == "Op");
  62. return clang::OO_Percent;
  63. }
  64. // Bitwise Operators.
  65. if (interface_name == "BitAndWith") {
  66. CARBON_CHECK(op_name == "Op");
  67. return clang::OO_Amp;
  68. }
  69. if (interface_name == "BitOrWith") {
  70. CARBON_CHECK(op_name == "Op");
  71. return clang::OO_Pipe;
  72. }
  73. if (interface_name == "BitXorWith") {
  74. CARBON_CHECK(op_name == "Op");
  75. return clang::OO_Caret;
  76. }
  77. if (interface_name == "LeftShiftWith") {
  78. CARBON_CHECK(op_name == "Op");
  79. return clang::OO_LessLess;
  80. }
  81. if (interface_name == "RightShiftWith") {
  82. CARBON_CHECK(op_name == "Op");
  83. return clang::OO_GreaterGreater;
  84. }
  85. // Compound Assignment Arithmetic Operators.
  86. if (interface_name == "AddAssignWith") {
  87. CARBON_CHECK(op_name == "Op");
  88. return clang::OO_PlusEqual;
  89. }
  90. if (interface_name == "SubAssignWith") {
  91. CARBON_CHECK(op_name == "Op");
  92. return clang::OO_MinusEqual;
  93. }
  94. if (interface_name == "MulAssignWith") {
  95. CARBON_CHECK(op_name == "Op");
  96. return clang::OO_StarEqual;
  97. }
  98. if (interface_name == "DivAssignWith") {
  99. CARBON_CHECK(op_name == "Op");
  100. return clang::OO_SlashEqual;
  101. }
  102. if (interface_name == "ModAssignWith") {
  103. CARBON_CHECK(op_name == "Op");
  104. return clang::OO_PercentEqual;
  105. }
  106. // Compound Assignment Bitwise Operators.
  107. if (interface_name == "BitAndAssignWith") {
  108. CARBON_CHECK(op_name == "Op");
  109. return clang::OO_AmpEqual;
  110. }
  111. if (interface_name == "BitOrAssignWith") {
  112. CARBON_CHECK(op_name == "Op");
  113. return clang::OO_PipeEqual;
  114. }
  115. if (interface_name == "BitXorAssignWith") {
  116. CARBON_CHECK(op_name == "Op");
  117. return clang::OO_CaretEqual;
  118. }
  119. if (interface_name == "LeftShiftAssignWith") {
  120. CARBON_CHECK(op_name == "Op");
  121. return clang::OO_LessLessEqual;
  122. }
  123. if (interface_name == "RightShiftAssignWith") {
  124. CARBON_CHECK(op_name == "Op");
  125. return clang::OO_GreaterGreaterEqual;
  126. }
  127. // Relational Operators.
  128. if (interface_name == "EqWith") {
  129. if (op_name == "Equal") {
  130. return clang::OO_EqualEqual;
  131. }
  132. CARBON_CHECK(op_name == "NotEqual");
  133. return clang::OO_ExclaimEqual;
  134. }
  135. if (interface_name == "OrderedWith") {
  136. if (op_name == "Less") {
  137. return clang::OO_Less;
  138. }
  139. if (op_name == "Greater") {
  140. return clang::OO_Greater;
  141. }
  142. if (op_name == "LessOrEquivalent") {
  143. return clang::OO_LessEqual;
  144. }
  145. CARBON_CHECK(op_name == "GreaterOrEquivalent");
  146. return clang::OO_GreaterEqual;
  147. }
  148. context.TODO(loc_id, llvm::formatv("Unsupported operator interface `{0}`",
  149. interface_name));
  150. return std::nullopt;
  151. }
  152. auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
  153. llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
  154. // Register an annotation scope to flush any Clang diagnostics when we return.
  155. // This is important to ensure that Clang diagnostics are properly interleaved
  156. // with Carbon diagnostics.
  157. Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
  158. [](auto& /*builder*/) {});
  159. auto op_kind =
  160. GetClangOperatorKind(context, loc_id, op.interface_name, op.op_name);
  161. if (!op_kind) {
  162. return SemIR::InstId::None;
  163. }
  164. // Make sure all operands are complete before lookup.
  165. for (SemIR::InstId arg_id : arg_ids) {
  166. SemIR::TypeId arg_type_id = context.insts().Get(arg_id).type_id();
  167. if (!RequireCompleteType(context, arg_type_id, loc_id, [&] {
  168. CARBON_DIAGNOSTIC(
  169. IncompleteOperandTypeInCppOperatorLookup, Error,
  170. "looking up a C++ operator with incomplete operand type {0}",
  171. SemIR::TypeId);
  172. return context.emitter().Build(
  173. loc_id, IncompleteOperandTypeInCppOperatorLookup, arg_type_id);
  174. })) {
  175. return SemIR::ErrorInst::InstId;
  176. }
  177. }
  178. auto maybe_arg_exprs = InventClangArgs(context, arg_ids);
  179. if (!maybe_arg_exprs.has_value()) {
  180. return SemIR::ErrorInst::InstId;
  181. }
  182. auto& arg_exprs = *maybe_arg_exprs;
  183. clang::SourceLocation loc = GetCppLocation(context, loc_id);
  184. clang::OverloadCandidateSet::OperatorRewriteInfo operator_rewrite_info(
  185. *op_kind, loc, /*AllowRewritten=*/true);
  186. clang::OverloadCandidateSet candidate_set(
  187. loc, clang::OverloadCandidateSet::CSK_Operator, operator_rewrite_info);
  188. clang::Sema& sema = context.clang_sema();
  189. // This works for both unary and binary operators.
  190. sema.LookupOverloadedBinOp(candidate_set, *op_kind, clang::UnresolvedSet<0>{},
  191. arg_exprs);
  192. clang::OverloadCandidateSet::iterator best_viable_fn;
  193. switch (candidate_set.BestViableFunction(sema, loc, best_viable_fn)) {
  194. case clang::OverloadingResult::OR_Success: {
  195. if (!best_viable_fn->Function) {
  196. // The best viable candidate was a builtin. Let the Carbon operator
  197. // machinery handle that.
  198. return SemIR::InstId::None;
  199. }
  200. if (best_viable_fn->RewriteKind) {
  201. context.TODO(
  202. loc_id,
  203. llvm::formatv("Rewriting operator{0} using {1} is not supported",
  204. clang::getOperatorSpelling(
  205. candidate_set.getRewriteInfo().OriginalOperator),
  206. best_viable_fn->Function->getNameAsString()));
  207. return SemIR::ErrorInst::InstId;
  208. }
  209. sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
  210. auto result_id = ImportCppFunctionDecl(
  211. context, loc_id, best_viable_fn->Function,
  212. // If this is an operator method, the first arg will be used as self.
  213. arg_ids.size() -
  214. (isa<clang::CXXMethodDecl>(best_viable_fn->Function) ? 1 : 0));
  215. if (auto fn_decl =
  216. context.insts().TryGetAsWithId<SemIR::FunctionDecl>(result_id)) {
  217. CheckCppOverloadAccess(context, loc_id, best_viable_fn->FoundDecl,
  218. fn_decl->inst_id);
  219. } else {
  220. CARBON_CHECK(result_id == SemIR::ErrorInst::InstId);
  221. }
  222. return result_id;
  223. }
  224. case clang::OverloadingResult::OR_No_Viable_Function: {
  225. // OK, didn't find a viable C++ candidate, but this is not an error, as
  226. // there might be a Carbon candidate.
  227. return SemIR::InstId::None;
  228. }
  229. case clang::OverloadingResult::OR_Ambiguous: {
  230. const char* spelling = clang::getOperatorSpelling(*op_kind);
  231. candidate_set.NoteCandidates(
  232. clang::PartialDiagnosticAt(
  233. loc, sema.PDiag(clang::diag::err_ovl_ambiguous_oper_binary)
  234. << spelling << arg_exprs[0]->getType()
  235. << arg_exprs[1]->getType()),
  236. sema, clang::OCD_AmbiguousCandidates, arg_exprs, spelling, loc);
  237. return SemIR::ErrorInst::InstId;
  238. }
  239. case clang::OverloadingResult::OR_Deleted:
  240. const char* spelling = clang::getOperatorSpelling(*op_kind);
  241. auto* message = best_viable_fn->Function->getDeletedMessage();
  242. // The best viable function might be a different operator if the best
  243. // candidate is a rewritten candidate, so use the operator kind of the
  244. // candidate itself in the diagnostic.
  245. candidate_set.NoteCandidates(
  246. clang::PartialDiagnosticAt(
  247. loc, sema.PDiag(clang::diag::err_ovl_deleted_oper)
  248. << clang::getOperatorSpelling(
  249. best_viable_fn->Function->getOverloadedOperator())
  250. << (message != nullptr)
  251. << (message ? message->getString() : llvm::StringRef())),
  252. sema, clang::OCD_AllCandidates, arg_exprs, spelling, loc);
  253. return SemIR::ErrorInst::InstId;
  254. }
  255. }
  256. auto IsCppOperatorMethodDecl(clang::Decl* decl) -> bool {
  257. auto* clang_method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
  258. return clang_method_decl && clang_method_decl->isOverloadedOperator();
  259. }
  260. static auto GetAsCppFunctionDecl(Context& context, SemIR::InstId inst_id)
  261. -> clang::FunctionDecl* {
  262. auto function_type = context.types().TryGetAs<SemIR::FunctionType>(
  263. context.insts().Get(inst_id).type_id());
  264. if (!function_type) {
  265. return nullptr;
  266. }
  267. SemIR::ClangDeclId clang_decl_id =
  268. context.functions().Get(function_type->function_id).clang_decl_id;
  269. return clang_decl_id.has_value()
  270. ? dyn_cast<clang::FunctionDecl>(
  271. context.clang_decls().Get(clang_decl_id).key.decl)
  272. : nullptr;
  273. }
  274. auto IsCppOperatorMethod(Context& context, SemIR::InstId inst_id) -> bool {
  275. auto* function_decl = GetAsCppFunctionDecl(context, inst_id);
  276. return function_decl && IsCppOperatorMethodDecl(function_decl);
  277. }
  278. auto IsCppConstructorOrNonMethodOperator(Context& context,
  279. SemIR::InstId inst_id) -> bool {
  280. auto* function_decl = GetAsCppFunctionDecl(context, inst_id);
  281. if (!function_decl) {
  282. return false;
  283. }
  284. if (isa<clang::CXXConstructorDecl>(function_decl)) {
  285. return true;
  286. }
  287. return !isa<clang::CXXMethodDecl>(function_decl) &&
  288. function_decl->isOverloadedOperator();
  289. }
  290. } // namespace Carbon::Check