mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-02 17:52:58 +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:
@@ -527,41 +527,6 @@ static auto ClangLookup(Context& context, SemIR::NameScopeId scope_id,
|
||||
return lookup;
|
||||
}
|
||||
|
||||
// Looks up the given declaration name in the Clang AST in a specific scope.
|
||||
// Returns the found declaration and its access. If not found, returns
|
||||
// `nullopt`. If there's not a single result, returns `nullptr` and default
|
||||
// access.
|
||||
static auto ClangLookupDeclarationName(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::NameScopeId scope_id,
|
||||
clang::DeclarationName name)
|
||||
-> std::optional<std::tuple<clang::NamedDecl*, clang::AccessSpecifier>> {
|
||||
auto lookup = ClangLookup(context, scope_id, name);
|
||||
if (!lookup) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::tuple<clang::NamedDecl*, clang::AccessSpecifier> result{
|
||||
nullptr, clang::AccessSpecifier::AS_none};
|
||||
|
||||
// Access checks are performed separately by the Carbon name lookup logic.
|
||||
lookup->suppressAccessDiagnostics();
|
||||
|
||||
if (!lookup->isSingleResult()) {
|
||||
// Clang will diagnose ambiguous lookup results for us.
|
||||
if (!lookup->isAmbiguous()) {
|
||||
context.TODO(loc_id,
|
||||
llvm::formatv("Unsupported: Lookup succeeded but couldn't "
|
||||
"find a single result; LookupResultKind: {0}",
|
||||
static_cast<int>(lookup->getResultKind())));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result = {lookup->getFoundDecl(), lookup->begin().getAccess()};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Looks up for constructors in the class scope and returns the lookup result.
|
||||
static auto ClangConstructorLookup(Context& context,
|
||||
SemIR::NameScopeId scope_id)
|
||||
@@ -1894,11 +1859,8 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
|
||||
|
||||
// Attempts to import a set of declarations. Returns `false` if an error was
|
||||
// produced, `true` otherwise.
|
||||
// TODO: Merge overload set and operators and remove the `is_overload_set`
|
||||
// param.
|
||||
static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
|
||||
ImportWorklist& worklist,
|
||||
bool is_overload_set = false) -> bool {
|
||||
ImportWorklist& worklist) -> bool {
|
||||
// Walk the dependency graph in depth-first order, and import declarations
|
||||
// once we've imported all of their dependencies.
|
||||
while (!worklist.empty()) {
|
||||
@@ -1924,7 +1886,7 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
|
||||
// Functions that are part of the overload set are imported at a later
|
||||
// point, once the overload resolution has selected the suitable function
|
||||
// for the call.
|
||||
if (is_overload_set && decl->getAsFunction()) {
|
||||
if (decl->getAsFunction()) {
|
||||
continue;
|
||||
}
|
||||
auto inst_id = ImportDeclAfterDependencies(context, loc_id, decl);
|
||||
@@ -2056,10 +2018,9 @@ static auto LookupBuiltinTypes(Context& context, SemIR::LocId loc_id,
|
||||
return inst_id;
|
||||
}
|
||||
|
||||
// Imports an overloaded function set from Clang to Carbon.
|
||||
static auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,
|
||||
SemIR::NameId name_id,
|
||||
const clang::UnresolvedSet<4>& overload_set)
|
||||
auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,
|
||||
SemIR::NameId name_id,
|
||||
const clang::UnresolvedSet<4>& overload_set)
|
||||
-> SemIR::InstId {
|
||||
SemIR::CppOverloadSetId overload_set_id = context.cpp_overload_sets().Add(
|
||||
SemIR::CppOverloadSet{.name_id = name_id,
|
||||
@@ -2102,7 +2063,7 @@ static auto ImportOverloadSetAndDependencies(
|
||||
for (clang::NamedDecl* fn_decl : overloaded_set) {
|
||||
AddDependentDecl(context, fn_decl, worklist);
|
||||
}
|
||||
if (!ImportDeclSet(context, loc_id, worklist, true)) {
|
||||
if (!ImportDeclSet(context, loc_id, worklist)) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
return ImportCppOverloadSet(context, scope_id, name_id, overloaded_set);
|
||||
@@ -2213,187 +2174,6 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
|
||||
overload_set);
|
||||
}
|
||||
|
||||
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 ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::NameScopeId scope_id, Operator op)
|
||||
-> SemIR::ScopeLookupResult {
|
||||
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::ScopeLookupResult::MakeNotFound();
|
||||
}
|
||||
|
||||
// TODO: We should do ADL-only lookup for operators
|
||||
// (`Sema::ArgumentDependentLookup`), when we support mapping Carbon types
|
||||
// into C++ types. See
|
||||
// https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2316950123
|
||||
auto decl_and_access = ClangLookupDeclarationName(
|
||||
context, loc_id, scope_id,
|
||||
context.ast_context().DeclarationNames.getCXXOperatorName(*op_kind));
|
||||
|
||||
if (!decl_and_access) {
|
||||
return SemIR::ScopeLookupResult::MakeNotFound();
|
||||
}
|
||||
auto [decl, access] = *decl_and_access;
|
||||
if (!decl) {
|
||||
return SemIR::ScopeLookupResult::MakeError();
|
||||
}
|
||||
|
||||
SemIR::InstId inst_id = ImportDeclAndDependencies(context, loc_id, decl);
|
||||
if (!inst_id.has_value()) {
|
||||
return SemIR::ScopeLookupResult::MakeNotFound();
|
||||
}
|
||||
|
||||
SemIR::AccessKind access_kind = MapAccess(access);
|
||||
return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id,
|
||||
access_kind);
|
||||
}
|
||||
|
||||
auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::ClassId class_id,
|
||||
SemIR::ClangDeclId clang_decl_id)
|
||||
|
||||
Reference in New Issue
Block a user