operators.cpp 12 KB

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