mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Overload resolution for C++ operators (#6092)
Multiple overloads for the same operator are now resolved using overload resolution. This change doesn't try to solve all issues with operator lookup. Moved the operator lookup logic from `import` to `operators` and changed it to take the args into account. Use `Sema::LookupOverloadedBinOp()` (with ADL) when looking up operator functions to create an overload set. Verified all demos in #6017, #6020 and #6024 still work. C++ Interop Demo: ```c++ // my_number.h class MyNumber { public: explicit MyNumber(int value) : value_(value) {} auto value() const -> int { return value_; } private: int value_; }; class NotMyNumber {}; auto operator+(MyNumber lhs, MyNumber rhs) -> MyNumber; auto operator+(NotMyNumber lhs, NotMyNumber rhs) -> NotMyNumber; ``` ```c++ // my_number.cpp #include "my_number.h" auto operator+(MyNumber lhs, MyNumber rhs) -> MyNumber { return MyNumber(lhs.value() + rhs.value()); } auto operator+(NotMyNumber lhs, NotMyNumber /*rhs*/) -> NotMyNumber { return lhs; } ``` ```carbon // main.carbon library "Main"; import Core library "io"; import Cpp library "my_number.h"; fn Run() -> i32 { // Arithmetic var num1: Cpp.MyNumber = Cpp.MyNumber.MyNumber(14); var num2: Cpp.MyNumber = Cpp.MyNumber.MyNumber(5); Core.Print(num1.value()); Core.Print(num2.value()); Core.Print((num1 + num2).value()); return 0; } ``` **After this change:** ```shell $ clang -c my_number.cpp $ bazel-bin/toolchain/carbon compile main.carbon $ bazel-bin/toolchain/carbon link my_number.o main.o --output=demo $ ./demo 14 5 19 ``` **Before this change** ```shell $ bazel-bin/toolchain/carbon compile main.carbon main.carbon:14:15: error: semantics TODO: `Unsupported: Lookup succeeded but couldn't find a single result; LookupResultKind: 3` Core.Print((num1 + num2).value()); ^~~~~~~~~~~ main.carbon:14:15: note: in `Cpp` operator `AddWith` lookup Core.Print((num1 + num2).value()); ^~~~~~~~~~~ ``` Part of https://github.com/carbon-language/carbon-lang/issues/5995.
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
// 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/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;
|
||||
}
|
||||
|
||||
auto arg_exprs = InventClangArgs(context, arg_ids);
|
||||
if (!arg_exprs.has_value()) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
clang::Sema& sema = context.sem_ir().clang_ast_unit()->getSema();
|
||||
|
||||
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.
|
||||
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, functions);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
Reference in New Issue
Block a user