| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #include "toolchain/check/cpp/operators.h"
- #include "clang/Sema/Overload.h"
- #include "clang/Sema/Sema.h"
- #include "toolchain/check/core_identifier.h"
- #include "toolchain/check/cpp/import.h"
- #include "toolchain/check/cpp/location.h"
- #include "toolchain/check/cpp/overload_resolution.h"
- #include "toolchain/check/cpp/type_mapping.h"
- #include "toolchain/check/inst.h"
- #include "toolchain/check/type.h"
- #include "toolchain/check/type_completion.h"
- #include "toolchain/sem_ir/ids.h"
- #include "toolchain/sem_ir/typed_insts.h"
- namespace Carbon::Check {
- // Maps Carbon operator interface and operator names to Clang operator kinds.
- static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id,
- CoreIdentifier interface_name,
- CoreIdentifier op_name)
- -> std::optional<clang::OverloadedOperatorKind> {
- switch (interface_name) {
- // Unary operators.
- case CoreIdentifier::Destroy:
- case CoreIdentifier::As:
- case CoreIdentifier::ImplicitAs:
- case CoreIdentifier::Copy: {
- // TODO: Support destructors and conversions.
- return std::nullopt;
- }
- // Increment and decrement.
- case CoreIdentifier::Inc: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_PlusPlus;
- }
- case CoreIdentifier::Dec: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_MinusMinus;
- }
- // Arithmetic.
- case CoreIdentifier::Negate: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Minus;
- }
- // Binary operators.
- // Arithmetic operators.
- case CoreIdentifier::AddWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Plus;
- }
- case CoreIdentifier::SubWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Minus;
- }
- case CoreIdentifier::MulWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Star;
- }
- case CoreIdentifier::DivWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Slash;
- }
- case CoreIdentifier::ModWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Percent;
- }
- // Bitwise operators.
- case CoreIdentifier::BitAndWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Amp;
- }
- case CoreIdentifier::BitOrWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Pipe;
- }
- case CoreIdentifier::BitXorWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_Caret;
- }
- case CoreIdentifier::LeftShiftWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_LessLess;
- }
- case CoreIdentifier::RightShiftWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_GreaterGreater;
- }
- // Compound assignment arithmetic operators.
- case CoreIdentifier::AddAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_PlusEqual;
- }
- case CoreIdentifier::SubAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_MinusEqual;
- }
- case CoreIdentifier::MulAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_StarEqual;
- }
- case CoreIdentifier::DivAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_SlashEqual;
- }
- case CoreIdentifier::ModAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_PercentEqual;
- }
- // Compound assignment bitwise operators.
- case CoreIdentifier::BitAndAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_AmpEqual;
- }
- case CoreIdentifier::BitOrAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_PipeEqual;
- }
- case CoreIdentifier::BitXorAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_CaretEqual;
- }
- case CoreIdentifier::LeftShiftAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_LessLessEqual;
- }
- case CoreIdentifier::RightShiftAssignWith: {
- CARBON_CHECK(op_name == CoreIdentifier::Op);
- return clang::OO_GreaterGreaterEqual;
- }
- // Relational operators.
- case CoreIdentifier::EqWith: {
- if (op_name == CoreIdentifier::Equal) {
- return clang::OO_EqualEqual;
- }
- CARBON_CHECK(op_name == CoreIdentifier::NotEqual);
- return clang::OO_ExclaimEqual;
- }
- case CoreIdentifier::OrderedWith: {
- switch (op_name) {
- case CoreIdentifier::Less:
- return clang::OO_Less;
- case CoreIdentifier::Greater:
- return clang::OO_Greater;
- case CoreIdentifier::LessOrEquivalent:
- return clang::OO_LessEqual;
- case CoreIdentifier::GreaterOrEquivalent:
- return clang::OO_GreaterEqual;
- default:
- CARBON_FATAL("Unexpected OrderedWith op `{0}`", op_name);
- }
- }
- default:
- context.TODO(loc_id, llvm::formatv("Unsupported operator interface `{0}`",
- interface_name));
- return std::nullopt;
- }
- }
- auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
- llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
- // Register an annotation scope to flush any Clang diagnostics when we return.
- // This is important to ensure that Clang diagnostics are properly interleaved
- // with Carbon diagnostics.
- Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
- [](auto& /*builder*/) {});
- auto op_kind =
- GetClangOperatorKind(context, loc_id, op.interface_name, op.op_name);
- if (!op_kind) {
- return SemIR::InstId::None;
- }
- // Make sure all operands are complete before lookup.
- for (SemIR::InstId arg_id : arg_ids) {
- SemIR::TypeId arg_type_id = context.insts().Get(arg_id).type_id();
- if (!RequireCompleteType(context, arg_type_id, loc_id, [&] {
- CARBON_DIAGNOSTIC(
- IncompleteOperandTypeInCppOperatorLookup, Error,
- "looking up a C++ operator with incomplete operand type {0}",
- SemIR::TypeId);
- return context.emitter().Build(
- loc_id, IncompleteOperandTypeInCppOperatorLookup, arg_type_id);
- })) {
- return SemIR::ErrorInst::InstId;
- }
- }
- auto maybe_arg_exprs = InventClangArgs(context, arg_ids);
- if (!maybe_arg_exprs.has_value()) {
- return SemIR::ErrorInst::InstId;
- }
- auto& arg_exprs = *maybe_arg_exprs;
- clang::SourceLocation loc = GetCppLocation(context, loc_id);
- clang::OverloadCandidateSet::OperatorRewriteInfo operator_rewrite_info(
- *op_kind, loc, /*AllowRewritten=*/true);
- clang::OverloadCandidateSet candidate_set(
- loc, clang::OverloadCandidateSet::CSK_Operator, operator_rewrite_info);
- clang::Sema& sema = context.clang_sema();
- // This works for both unary and binary operators.
- sema.LookupOverloadedBinOp(candidate_set, *op_kind, clang::UnresolvedSet<0>{},
- arg_exprs);
- clang::OverloadCandidateSet::iterator best_viable_fn;
- switch (candidate_set.BestViableFunction(sema, loc, best_viable_fn)) {
- case clang::OverloadingResult::OR_Success: {
- if (!best_viable_fn->Function) {
- // The best viable candidate was a builtin. Let the Carbon operator
- // machinery handle that.
- return SemIR::InstId::None;
- }
- if (best_viable_fn->RewriteKind) {
- context.TODO(
- loc_id,
- llvm::formatv("Rewriting operator{0} using {1} is not supported",
- clang::getOperatorSpelling(
- candidate_set.getRewriteInfo().OriginalOperator),
- best_viable_fn->Function->getNameAsString()));
- return SemIR::ErrorInst::InstId;
- }
- sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
- auto result_id = ImportCppFunctionDecl(
- context, loc_id, best_viable_fn->Function,
- // If this is an operator method, the first arg will be used as self.
- arg_ids.size() -
- (isa<clang::CXXMethodDecl>(best_viable_fn->Function) ? 1 : 0));
- if (auto fn_decl =
- context.insts().TryGetAsWithId<SemIR::FunctionDecl>(result_id)) {
- CheckCppOverloadAccess(context, loc_id, best_viable_fn->FoundDecl,
- fn_decl->inst_id);
- } else {
- CARBON_CHECK(result_id == SemIR::ErrorInst::InstId);
- }
- return result_id;
- }
- case clang::OverloadingResult::OR_No_Viable_Function: {
- // OK, didn't find a viable C++ candidate, but this is not an error, as
- // there might be a Carbon candidate.
- return SemIR::InstId::None;
- }
- case clang::OverloadingResult::OR_Ambiguous: {
- const char* spelling = clang::getOperatorSpelling(*op_kind);
- candidate_set.NoteCandidates(
- clang::PartialDiagnosticAt(
- loc, sema.PDiag(clang::diag::err_ovl_ambiguous_oper_binary)
- << spelling << arg_exprs[0]->getType()
- << arg_exprs[1]->getType()),
- sema, clang::OCD_AmbiguousCandidates, arg_exprs, spelling, loc);
- return SemIR::ErrorInst::InstId;
- }
- case clang::OverloadingResult::OR_Deleted:
- const char* spelling = clang::getOperatorSpelling(*op_kind);
- auto* message = best_viable_fn->Function->getDeletedMessage();
- // The best viable function might be a different operator if the best
- // candidate is a rewritten candidate, so use the operator kind of the
- // candidate itself in the diagnostic.
- candidate_set.NoteCandidates(
- clang::PartialDiagnosticAt(
- loc, sema.PDiag(clang::diag::err_ovl_deleted_oper)
- << clang::getOperatorSpelling(
- best_viable_fn->Function->getOverloadedOperator())
- << (message != nullptr)
- << (message ? message->getString() : llvm::StringRef())),
- sema, clang::OCD_AllCandidates, arg_exprs, spelling, loc);
- return SemIR::ErrorInst::InstId;
- }
- }
- auto IsCppOperatorMethodDecl(clang::Decl* decl) -> bool {
- auto* clang_method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
- return clang_method_decl && clang_method_decl->isOverloadedOperator();
- }
- static auto GetAsCppFunctionDecl(Context& context, SemIR::InstId inst_id)
- -> clang::FunctionDecl* {
- auto function_type = context.types().TryGetAs<SemIR::FunctionType>(
- context.insts().Get(inst_id).type_id());
- if (!function_type) {
- return nullptr;
- }
- SemIR::ClangDeclId clang_decl_id =
- context.functions().Get(function_type->function_id).clang_decl_id;
- return clang_decl_id.has_value()
- ? dyn_cast<clang::FunctionDecl>(
- context.clang_decls().Get(clang_decl_id).key.decl)
- : nullptr;
- }
- auto IsCppOperatorMethod(Context& context, SemIR::InstId inst_id) -> bool {
- auto* function_decl = GetAsCppFunctionDecl(context, inst_id);
- return function_decl && IsCppOperatorMethodDecl(function_decl);
- }
- auto IsCppConstructorOrNonMethodOperator(Context& context,
- SemIR::InstId inst_id) -> bool {
- auto* function_decl = GetAsCppFunctionDecl(context, inst_id);
- if (!function_decl) {
- return false;
- }
- if (isa<clang::CXXConstructorDecl>(function_decl)) {
- return true;
- }
- return !isa<clang::CXXMethodDecl>(function_decl) &&
- function_decl->isOverloadedOperator();
- }
- } // namespace Carbon::Check
|