mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Instead of calling `PerformCppOverloadResolution()` and use the complex return value to call `PerformCallToFunction()`, we call `PerformCallToCppFunction()` which will call both `PerformCppOverloadResolution()` and `PerformCallToFunction()`. Followup of #6112. Part of #5995.
219 lines
6.7 KiB
C++
219 lines
6.7 KiB
C++
// 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/cpp/import.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"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// Maps Carbon operator interface and operator names to Clang operator kinds.
|
|
static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id,
|
|
llvm::StringLiteral interface_name,
|
|
llvm::StringLiteral op_name)
|
|
-> std::optional<clang::OverloadedOperatorKind> {
|
|
// Unary operators.
|
|
if (interface_name == "Destroy" || interface_name == "As" ||
|
|
interface_name == "ImplicitAs") {
|
|
// TODO: Support destructors and conversions.
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Increment and Decrement.
|
|
if (interface_name == "Inc") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_PlusPlus;
|
|
}
|
|
if (interface_name == "Dec") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_MinusMinus;
|
|
}
|
|
|
|
// Arithmetic.
|
|
if (interface_name == "Negate") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Minus;
|
|
}
|
|
|
|
// Binary operators.
|
|
|
|
// Arithmetic Operators.
|
|
if (interface_name == "AddWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Plus;
|
|
}
|
|
if (interface_name == "SubWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Minus;
|
|
}
|
|
if (interface_name == "MulWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Star;
|
|
}
|
|
if (interface_name == "DivWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Slash;
|
|
}
|
|
if (interface_name == "ModWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Percent;
|
|
}
|
|
|
|
// Bitwise Operators.
|
|
if (interface_name == "BitAndWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Amp;
|
|
}
|
|
if (interface_name == "BitOrWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Pipe;
|
|
}
|
|
if (interface_name == "BitXorWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_Caret;
|
|
}
|
|
if (interface_name == "LeftShiftWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_LessLess;
|
|
}
|
|
if (interface_name == "RightShiftWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_GreaterGreater;
|
|
}
|
|
|
|
// Compound Assignment Arithmetic Operators.
|
|
if (interface_name == "AddAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_PlusEqual;
|
|
}
|
|
if (interface_name == "SubAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_MinusEqual;
|
|
}
|
|
if (interface_name == "MulAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_StarEqual;
|
|
}
|
|
if (interface_name == "DivAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_SlashEqual;
|
|
}
|
|
if (interface_name == "ModAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_PercentEqual;
|
|
}
|
|
|
|
// Compound Assignment Bitwise Operators.
|
|
if (interface_name == "BitAndAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_AmpEqual;
|
|
}
|
|
if (interface_name == "BitOrAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_PipeEqual;
|
|
}
|
|
if (interface_name == "BitXorAssignWith") {
|
|
CARBON_CHECK(op_name == "Op");
|
|
return clang::OO_CaretEqual;
|
|
}
|
|
// TODO: Add support for `LeftShiftAssignWith` (`OO_LessLessEqual`) and
|
|
// `RightShiftAssignWith` (`OO_GreaterGreaterEqual`) when references are
|
|
// supported.
|
|
|
|
// Relational Operators.
|
|
if (interface_name == "EqWith") {
|
|
if (op_name == "Equal") {
|
|
return clang::OO_EqualEqual;
|
|
}
|
|
CARBON_CHECK(op_name == "NotEqual");
|
|
return clang::OO_ExclaimEqual;
|
|
}
|
|
if (interface_name == "OrderedWith") {
|
|
if (op_name == "Less") {
|
|
return clang::OO_Less;
|
|
}
|
|
if (op_name == "Greater") {
|
|
return clang::OO_Greater;
|
|
}
|
|
if (op_name == "LessOrEquivalent") {
|
|
return clang::OO_LessEqual;
|
|
}
|
|
CARBON_CHECK(op_name == "GreaterOrEquivalent");
|
|
return clang::OO_GreaterEqual;
|
|
}
|
|
|
|
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 {
|
|
Diagnostics::AnnotationScope annotate_diagnostics(
|
|
&context.emitter(), [&](auto& builder) {
|
|
CARBON_DIAGNOSTIC(InCppOperatorLookup, Note,
|
|
"in `Cpp` operator `{0}` lookup", std::string);
|
|
builder.Note(loc_id, InCppOperatorLookup, op.interface_name.str());
|
|
});
|
|
|
|
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 arg_exprs = InventClangArgs(context, arg_ids);
|
|
if (!arg_exprs.has_value()) {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
clang::UnresolvedSet<4> functions;
|
|
// TODO: Add location accordingly.
|
|
clang::OverloadCandidateSet candidate_set(
|
|
clang::SourceLocation(), clang::OverloadCandidateSet::CSK_Operator);
|
|
// This works for both unary and binary operators.
|
|
context.clang_sema().LookupOverloadedBinOp(candidate_set, *op_kind, functions,
|
|
*arg_exprs);
|
|
|
|
for (auto& it : candidate_set) {
|
|
if (!it.Function) {
|
|
continue;
|
|
}
|
|
functions.addDecl(it.Function, it.FoundDecl.getAccess());
|
|
}
|
|
|
|
return ImportCppOverloadSet(context, SemIR::NameScopeId::None,
|
|
SemIR::NameId::CppOperator,
|
|
/*naming_class=*/nullptr, std::move(functions));
|
|
}
|
|
|
|
auto IsCppOperatorMethodDecl(clang::Decl* decl) -> bool {
|
|
auto* clang_method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
|
|
return clang_method_decl && clang_method_decl->isOverloadedOperator();
|
|
}
|
|
|
|
} // namespace Carbon::Check
|