operators.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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/type_mapping.h"
  9. #include "toolchain/check/inst.h"
  10. #include "toolchain/check/type.h"
  11. #include "toolchain/check/type_completion.h"
  12. #include "toolchain/sem_ir/ids.h"
  13. namespace Carbon::Check {
  14. // Maps Carbon operator interface and operator names to Clang operator kinds.
  15. static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id,
  16. llvm::StringLiteral interface_name,
  17. llvm::StringLiteral op_name)
  18. -> std::optional<clang::OverloadedOperatorKind> {
  19. // Unary operators.
  20. if (interface_name == "Destroy" || interface_name == "As" ||
  21. interface_name == "ImplicitAs") {
  22. // TODO: Support destructors and conversions.
  23. return std::nullopt;
  24. }
  25. // Increment and Decrement.
  26. if (interface_name == "Inc") {
  27. CARBON_CHECK(op_name == "Op");
  28. return clang::OO_PlusPlus;
  29. }
  30. if (interface_name == "Dec") {
  31. CARBON_CHECK(op_name == "Op");
  32. return clang::OO_MinusMinus;
  33. }
  34. // Arithmetic.
  35. if (interface_name == "Negate") {
  36. CARBON_CHECK(op_name == "Op");
  37. return clang::OO_Minus;
  38. }
  39. // Binary operators.
  40. // Arithmetic Operators.
  41. if (interface_name == "AddWith") {
  42. CARBON_CHECK(op_name == "Op");
  43. return clang::OO_Plus;
  44. }
  45. if (interface_name == "SubWith") {
  46. CARBON_CHECK(op_name == "Op");
  47. return clang::OO_Minus;
  48. }
  49. if (interface_name == "MulWith") {
  50. CARBON_CHECK(op_name == "Op");
  51. return clang::OO_Star;
  52. }
  53. if (interface_name == "DivWith") {
  54. CARBON_CHECK(op_name == "Op");
  55. return clang::OO_Slash;
  56. }
  57. if (interface_name == "ModWith") {
  58. CARBON_CHECK(op_name == "Op");
  59. return clang::OO_Percent;
  60. }
  61. // Bitwise Operators.
  62. if (interface_name == "BitAndWith") {
  63. CARBON_CHECK(op_name == "Op");
  64. return clang::OO_Amp;
  65. }
  66. if (interface_name == "BitOrWith") {
  67. CARBON_CHECK(op_name == "Op");
  68. return clang::OO_Pipe;
  69. }
  70. if (interface_name == "BitXorWith") {
  71. CARBON_CHECK(op_name == "Op");
  72. return clang::OO_Caret;
  73. }
  74. if (interface_name == "LeftShiftWith") {
  75. CARBON_CHECK(op_name == "Op");
  76. return clang::OO_LessLess;
  77. }
  78. if (interface_name == "RightShiftWith") {
  79. CARBON_CHECK(op_name == "Op");
  80. return clang::OO_GreaterGreater;
  81. }
  82. // Compound Assignment Arithmetic Operators.
  83. if (interface_name == "AddAssignWith") {
  84. CARBON_CHECK(op_name == "Op");
  85. return clang::OO_PlusEqual;
  86. }
  87. if (interface_name == "SubAssignWith") {
  88. CARBON_CHECK(op_name == "Op");
  89. return clang::OO_MinusEqual;
  90. }
  91. if (interface_name == "MulAssignWith") {
  92. CARBON_CHECK(op_name == "Op");
  93. return clang::OO_StarEqual;
  94. }
  95. if (interface_name == "DivAssignWith") {
  96. CARBON_CHECK(op_name == "Op");
  97. return clang::OO_SlashEqual;
  98. }
  99. if (interface_name == "ModAssignWith") {
  100. CARBON_CHECK(op_name == "Op");
  101. return clang::OO_PercentEqual;
  102. }
  103. // Compound Assignment Bitwise Operators.
  104. if (interface_name == "BitAndAssignWith") {
  105. CARBON_CHECK(op_name == "Op");
  106. return clang::OO_AmpEqual;
  107. }
  108. if (interface_name == "BitOrAssignWith") {
  109. CARBON_CHECK(op_name == "Op");
  110. return clang::OO_PipeEqual;
  111. }
  112. if (interface_name == "BitXorAssignWith") {
  113. CARBON_CHECK(op_name == "Op");
  114. return clang::OO_CaretEqual;
  115. }
  116. // TODO: Add support for `LeftShiftAssignWith` (`OO_LessLessEqual`) and
  117. // `RightShiftAssignWith` (`OO_GreaterGreaterEqual`) when references are
  118. // supported.
  119. // Relational Operators.
  120. if (interface_name == "EqWith") {
  121. if (op_name == "Equal") {
  122. return clang::OO_EqualEqual;
  123. }
  124. CARBON_CHECK(op_name == "NotEqual");
  125. return clang::OO_ExclaimEqual;
  126. }
  127. if (interface_name == "OrderedWith") {
  128. if (op_name == "Less") {
  129. return clang::OO_Less;
  130. }
  131. if (op_name == "Greater") {
  132. return clang::OO_Greater;
  133. }
  134. if (op_name == "LessOrEquivalent") {
  135. return clang::OO_LessEqual;
  136. }
  137. CARBON_CHECK(op_name == "GreaterOrEquivalent");
  138. return clang::OO_GreaterEqual;
  139. }
  140. context.TODO(loc_id, llvm::formatv("Unsupported operator interface `{0}`",
  141. interface_name));
  142. return std::nullopt;
  143. }
  144. auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
  145. llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
  146. Diagnostics::AnnotationScope annotate_diagnostics(
  147. &context.emitter(), [&](auto& builder) {
  148. CARBON_DIAGNOSTIC(InCppOperatorLookup, Note,
  149. "in `Cpp` operator `{0}` lookup", std::string);
  150. builder.Note(loc_id, InCppOperatorLookup, op.interface_name.str());
  151. });
  152. auto op_kind =
  153. GetClangOperatorKind(context, loc_id, op.interface_name, op.op_name);
  154. if (!op_kind) {
  155. return SemIR::InstId::None;
  156. }
  157. // Make sure all operands are complete before lookup.
  158. for (SemIR::InstId arg_id : arg_ids) {
  159. SemIR::TypeId arg_type_id = context.insts().Get(arg_id).type_id();
  160. if (!RequireCompleteType(context, arg_type_id, loc_id, [&] {
  161. CARBON_DIAGNOSTIC(
  162. IncompleteOperandTypeInCppOperatorLookup, Error,
  163. "looking up a C++ operator with incomplete operand type {0}",
  164. SemIR::TypeId);
  165. return context.emitter().Build(
  166. loc_id, IncompleteOperandTypeInCppOperatorLookup, arg_type_id);
  167. })) {
  168. return SemIR::ErrorInst::InstId;
  169. }
  170. }
  171. auto arg_exprs = InventClangArgs(context, arg_ids);
  172. if (!arg_exprs.has_value()) {
  173. return SemIR::ErrorInst::InstId;
  174. }
  175. clang::UnresolvedSet<4> functions;
  176. // TODO: Add location accordingly.
  177. clang::OverloadCandidateSet candidate_set(
  178. clang::SourceLocation(), clang::OverloadCandidateSet::CSK_Operator);
  179. // This works for both unary and binary operators.
  180. context.clang_sema().LookupOverloadedBinOp(candidate_set, *op_kind, functions,
  181. *arg_exprs);
  182. for (auto& it : candidate_set) {
  183. if (!it.Function) {
  184. continue;
  185. }
  186. functions.addDecl(it.Function, it.FoundDecl.getAccess());
  187. }
  188. return ImportCppOverloadSet(context, SemIR::NameScopeId::None,
  189. SemIR::NameId::CppOperator,
  190. /*naming_class=*/nullptr, std::move(functions));
  191. }
  192. } // namespace Carbon::Check