Skip to content

Commit ef488f0

Browse files
authored
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 #5995.
1 parent 05c9fd7 commit ef488f0

File tree

10 files changed

+445
-426
lines changed

10 files changed

+445
-426
lines changed

toolchain/check/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ cc_library(
2323
"convert.cpp",
2424
"cpp/custom_type_mapping.cpp",
2525
"cpp/import.cpp",
26+
"cpp/operators.cpp",
2627
"cpp/overload_resolution.cpp",
2728
"cpp/thunk.cpp",
2829
"cpp/type_mapping.cpp",
@@ -72,6 +73,7 @@ cc_library(
7273
"convert.h",
7374
"cpp/custom_type_mapping.h",
7475
"cpp/import.h",
76+
"cpp/operators.h",
7577
"cpp/overload_resolution.h",
7678
"cpp/thunk.h",
7779
"cpp/type_mapping.h",

toolchain/check/cpp/import.cpp

Lines changed: 6 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -527,41 +527,6 @@ static auto ClangLookup(Context& context, SemIR::NameScopeId scope_id,
527527
return lookup;
528528
}
529529

530-
// Looks up the given declaration name in the Clang AST in a specific scope.
531-
// Returns the found declaration and its access. If not found, returns
532-
// `nullopt`. If there's not a single result, returns `nullptr` and default
533-
// access.
534-
static auto ClangLookupDeclarationName(Context& context, SemIR::LocId loc_id,
535-
SemIR::NameScopeId scope_id,
536-
clang::DeclarationName name)
537-
-> std::optional<std::tuple<clang::NamedDecl*, clang::AccessSpecifier>> {
538-
auto lookup = ClangLookup(context, scope_id, name);
539-
if (!lookup) {
540-
return std::nullopt;
541-
}
542-
543-
std::tuple<clang::NamedDecl*, clang::AccessSpecifier> result{
544-
nullptr, clang::AccessSpecifier::AS_none};
545-
546-
// Access checks are performed separately by the Carbon name lookup logic.
547-
lookup->suppressAccessDiagnostics();
548-
549-
if (!lookup->isSingleResult()) {
550-
// Clang will diagnose ambiguous lookup results for us.
551-
if (!lookup->isAmbiguous()) {
552-
context.TODO(loc_id,
553-
llvm::formatv("Unsupported: Lookup succeeded but couldn't "
554-
"find a single result; LookupResultKind: {0}",
555-
static_cast<int>(lookup->getResultKind())));
556-
}
557-
558-
return result;
559-
}
560-
561-
result = {lookup->getFoundDecl(), lookup->begin().getAccess()};
562-
return result;
563-
}
564-
565530
// Looks up for constructors in the class scope and returns the lookup result.
566531
static auto ClangConstructorLookup(Context& context,
567532
SemIR::NameScopeId scope_id)
@@ -1894,11 +1859,8 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
18941859

18951860
// Attempts to import a set of declarations. Returns `false` if an error was
18961861
// produced, `true` otherwise.
1897-
// TODO: Merge overload set and operators and remove the `is_overload_set`
1898-
// param.
18991862
static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
1900-
ImportWorklist& worklist,
1901-
bool is_overload_set = false) -> bool {
1863+
ImportWorklist& worklist) -> bool {
19021864
// Walk the dependency graph in depth-first order, and import declarations
19031865
// once we've imported all of their dependencies.
19041866
while (!worklist.empty()) {
@@ -1924,7 +1886,7 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
19241886
// Functions that are part of the overload set are imported at a later
19251887
// point, once the overload resolution has selected the suitable function
19261888
// for the call.
1927-
if (is_overload_set && decl->getAsFunction()) {
1889+
if (decl->getAsFunction()) {
19281890
continue;
19291891
}
19301892
auto inst_id = ImportDeclAfterDependencies(context, loc_id, decl);
@@ -2056,10 +2018,9 @@ static auto LookupBuiltinTypes(Context& context, SemIR::LocId loc_id,
20562018
return inst_id;
20572019
}
20582020

2059-
// Imports an overloaded function set from Clang to Carbon.
2060-
static auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,
2061-
SemIR::NameId name_id,
2062-
const clang::UnresolvedSet<4>& overload_set)
2021+
auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,
2022+
SemIR::NameId name_id,
2023+
const clang::UnresolvedSet<4>& overload_set)
20632024
-> SemIR::InstId {
20642025
SemIR::CppOverloadSetId overload_set_id = context.cpp_overload_sets().Add(
20652026
SemIR::CppOverloadSet{.name_id = name_id,
@@ -2102,7 +2063,7 @@ static auto ImportOverloadSetAndDependencies(
21022063
for (clang::NamedDecl* fn_decl : overloaded_set) {
21032064
AddDependentDecl(context, fn_decl, worklist);
21042065
}
2105-
if (!ImportDeclSet(context, loc_id, worklist, true)) {
2066+
if (!ImportDeclSet(context, loc_id, worklist)) {
21062067
return SemIR::ErrorInst::InstId;
21072068
}
21082069
return ImportCppOverloadSet(context, scope_id, name_id, overloaded_set);
@@ -2213,187 +2174,6 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
22132174
overload_set);
22142175
}
22152176

2216-
static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id,
2217-
llvm::StringLiteral interface_name,
2218-
llvm::StringLiteral op_name)
2219-
-> std::optional<clang::OverloadedOperatorKind> {
2220-
// Unary operators.
2221-
if (interface_name == "Destroy" || interface_name == "As" ||
2222-
interface_name == "ImplicitAs") {
2223-
// TODO: Support destructors and conversions.
2224-
return std::nullopt;
2225-
}
2226-
2227-
// Increment and Decrement.
2228-
if (interface_name == "Inc") {
2229-
CARBON_CHECK(op_name == "Op");
2230-
return clang::OO_PlusPlus;
2231-
}
2232-
if (interface_name == "Dec") {
2233-
CARBON_CHECK(op_name == "Op");
2234-
return clang::OO_MinusMinus;
2235-
}
2236-
2237-
// Arithmetic.
2238-
if (interface_name == "Negate") {
2239-
CARBON_CHECK(op_name == "Op");
2240-
return clang::OO_Minus;
2241-
}
2242-
2243-
// Binary operators.
2244-
2245-
// Arithmetic Operators.
2246-
if (interface_name == "AddWith") {
2247-
CARBON_CHECK(op_name == "Op");
2248-
return clang::OO_Plus;
2249-
}
2250-
if (interface_name == "SubWith") {
2251-
CARBON_CHECK(op_name == "Op");
2252-
return clang::OO_Minus;
2253-
}
2254-
if (interface_name == "MulWith") {
2255-
CARBON_CHECK(op_name == "Op");
2256-
return clang::OO_Star;
2257-
}
2258-
if (interface_name == "DivWith") {
2259-
CARBON_CHECK(op_name == "Op");
2260-
return clang::OO_Slash;
2261-
}
2262-
if (interface_name == "ModWith") {
2263-
CARBON_CHECK(op_name == "Op");
2264-
return clang::OO_Percent;
2265-
}
2266-
2267-
// Bitwise Operators.
2268-
if (interface_name == "BitAndWith") {
2269-
CARBON_CHECK(op_name == "Op");
2270-
return clang::OO_Amp;
2271-
}
2272-
if (interface_name == "BitOrWith") {
2273-
CARBON_CHECK(op_name == "Op");
2274-
return clang::OO_Pipe;
2275-
}
2276-
if (interface_name == "BitXorWith") {
2277-
CARBON_CHECK(op_name == "Op");
2278-
return clang::OO_Caret;
2279-
}
2280-
if (interface_name == "LeftShiftWith") {
2281-
CARBON_CHECK(op_name == "Op");
2282-
return clang::OO_LessLess;
2283-
}
2284-
if (interface_name == "RightShiftWith") {
2285-
CARBON_CHECK(op_name == "Op");
2286-
return clang::OO_GreaterGreater;
2287-
}
2288-
2289-
// Compound Assignment Arithmetic Operators.
2290-
if (interface_name == "AddAssignWith") {
2291-
CARBON_CHECK(op_name == "Op");
2292-
return clang::OO_PlusEqual;
2293-
}
2294-
if (interface_name == "SubAssignWith") {
2295-
CARBON_CHECK(op_name == "Op");
2296-
return clang::OO_MinusEqual;
2297-
}
2298-
if (interface_name == "MulAssignWith") {
2299-
CARBON_CHECK(op_name == "Op");
2300-
return clang::OO_StarEqual;
2301-
}
2302-
if (interface_name == "DivAssignWith") {
2303-
CARBON_CHECK(op_name == "Op");
2304-
return clang::OO_SlashEqual;
2305-
}
2306-
if (interface_name == "ModAssignWith") {
2307-
CARBON_CHECK(op_name == "Op");
2308-
return clang::OO_PercentEqual;
2309-
}
2310-
2311-
// Compound Assignment Bitwise Operators.
2312-
if (interface_name == "BitAndAssignWith") {
2313-
CARBON_CHECK(op_name == "Op");
2314-
return clang::OO_AmpEqual;
2315-
}
2316-
if (interface_name == "BitOrAssignWith") {
2317-
CARBON_CHECK(op_name == "Op");
2318-
return clang::OO_PipeEqual;
2319-
}
2320-
if (interface_name == "BitXorAssignWith") {
2321-
CARBON_CHECK(op_name == "Op");
2322-
return clang::OO_CaretEqual;
2323-
}
2324-
// TODO: Add support for `LeftShiftAssignWith` (`OO_LessLessEqual`) and
2325-
// `RightShiftAssignWith` (`OO_GreaterGreaterEqual`) when references are
2326-
// supported.
2327-
2328-
// Relational Operators.
2329-
if (interface_name == "EqWith") {
2330-
if (op_name == "Equal") {
2331-
return clang::OO_EqualEqual;
2332-
}
2333-
CARBON_CHECK(op_name == "NotEqual");
2334-
return clang::OO_ExclaimEqual;
2335-
}
2336-
if (interface_name == "OrderedWith") {
2337-
if (op_name == "Less") {
2338-
return clang::OO_Less;
2339-
}
2340-
if (op_name == "Greater") {
2341-
return clang::OO_Greater;
2342-
}
2343-
if (op_name == "LessOrEquivalent") {
2344-
return clang::OO_LessEqual;
2345-
}
2346-
CARBON_CHECK(op_name == "GreaterOrEquivalent");
2347-
return clang::OO_GreaterEqual;
2348-
}
2349-
2350-
context.TODO(loc_id, llvm::formatv("Unsupported operator interface `{0}`",
2351-
interface_name));
2352-
return std::nullopt;
2353-
}
2354-
2355-
auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id,
2356-
SemIR::NameScopeId scope_id, Operator op)
2357-
-> SemIR::ScopeLookupResult {
2358-
Diagnostics::AnnotationScope annotate_diagnostics(
2359-
&context.emitter(), [&](auto& builder) {
2360-
CARBON_DIAGNOSTIC(InCppOperatorLookup, Note,
2361-
"in `Cpp` operator `{0}` lookup", std::string);
2362-
builder.Note(loc_id, InCppOperatorLookup, op.interface_name.str());
2363-
});
2364-
2365-
auto op_kind =
2366-
GetClangOperatorKind(context, loc_id, op.interface_name, op.op_name);
2367-
if (!op_kind) {
2368-
return SemIR::ScopeLookupResult::MakeNotFound();
2369-
}
2370-
2371-
// TODO: We should do ADL-only lookup for operators
2372-
// (`Sema::ArgumentDependentLookup`), when we support mapping Carbon types
2373-
// into C++ types. See
2374-
// https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2316950123
2375-
auto decl_and_access = ClangLookupDeclarationName(
2376-
context, loc_id, scope_id,
2377-
context.ast_context().DeclarationNames.getCXXOperatorName(*op_kind));
2378-
2379-
if (!decl_and_access) {
2380-
return SemIR::ScopeLookupResult::MakeNotFound();
2381-
}
2382-
auto [decl, access] = *decl_and_access;
2383-
if (!decl) {
2384-
return SemIR::ScopeLookupResult::MakeError();
2385-
}
2386-
2387-
SemIR::InstId inst_id = ImportDeclAndDependencies(context, loc_id, decl);
2388-
if (!inst_id.has_value()) {
2389-
return SemIR::ScopeLookupResult::MakeNotFound();
2390-
}
2391-
2392-
SemIR::AccessKind access_kind = MapAccess(access);
2393-
return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id,
2394-
access_kind);
2395-
}
2396-
23972177
auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
23982178
SemIR::ClassId class_id,
23992179
SemIR::ClangDeclId clang_decl_id)

toolchain/check/cpp/import.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "llvm/Support/VirtualFileSystem.h"
1212
#include "toolchain/check/context.h"
1313
#include "toolchain/check/diagnostic_helpers.h"
14-
#include "toolchain/check/operator.h"
1514
#include "toolchain/diagnostics/diagnostic_emitter.h"
1615

1716
namespace Carbon::Check {
@@ -31,19 +30,19 @@ auto ImportCppFiles(Context& context,
3130
auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
3231
clang::FunctionDecl* clang_decl) -> SemIR::InstId;
3332

33+
// Imports an overloaded function set from Clang to Carbon.
34+
auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,
35+
SemIR::NameId name_id,
36+
const clang::UnresolvedSet<4>& overload_set)
37+
-> SemIR::InstId;
38+
3439
// Looks up the given name in the Clang AST generated when importing C++ code
3540
// and returns a lookup result. If using the injected class name (`X.X()`),
3641
// imports the class constructor as a function named as the class.
3742
auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
3843
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
3944
-> SemIR::ScopeLookupResult;
4045

41-
// Looks up the given operator in the Clang AST generated when importing C++
42-
// code and returns a lookup result.
43-
auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id,
44-
SemIR::NameScopeId scope_id, Operator op)
45-
-> SemIR::ScopeLookupResult;
46-
4746
// Given a Carbon class declaration that was imported from some kind of C++
4847
// declaration, such as a class or enum, attempt to import a corresponding class
4948
// definition. Returns true if nothing went wrong (whether or not a definition

0 commit comments

Comments
 (0)