Files
carbon-lang/toolchain/check/cpp/overload_resolution.cpp
T
Boaz Brickner ef488f00fa 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.
2025-09-19 16:42:46 +00:00

152 lines
6.3 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/overload_resolution.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/sem_ir/ids.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
// Adds the given overload candidates to the candidate set.
static auto AddOverloadCandidataes(clang::Sema& sema,
clang::OverloadCandidateSet& candidate_set,
const clang::UnresolvedSetImpl& functions,
clang::Expr* self_arg,
llvm::ArrayRef<clang::Expr*> args) -> void {
constexpr bool SuppressUserConversions = false;
constexpr bool PartialOverloading = false;
constexpr clang::TemplateArgumentListInfo* ExplicitTemplateArgs = nullptr;
for (auto found_decl : functions.pairs()) {
auto* decl = found_decl.getDecl()->getUnderlyingDecl();
auto* template_decl = dyn_cast<clang::FunctionTemplateDecl>(decl);
auto* fn_decl = template_decl ? template_decl->getTemplatedDecl()
: cast<clang::FunctionDecl>(decl);
auto* method_decl = dyn_cast<clang::CXXMethodDecl>(fn_decl);
if (method_decl && !method_decl->isStatic() &&
!isa<clang::CXXConstructorDecl>(fn_decl)) {
clang::QualType self_type;
clang::Expr::Classification self_classification;
if (self_arg) {
self_type = self_arg->getType();
self_classification = self_arg->Classify(sema.Context);
}
if (template_decl) {
sema.AddMethodTemplateCandidate(
template_decl, found_decl,
cast<clang::CXXRecordDecl>(template_decl->getDeclContext()),
ExplicitTemplateArgs, self_type, self_classification, args,
candidate_set, SuppressUserConversions, PartialOverloading);
} else {
sema.AddMethodCandidate(method_decl, found_decl,
method_decl->getParent(), self_type,
self_classification, args, candidate_set,
SuppressUserConversions, PartialOverloading);
}
} else {
if (template_decl) {
sema.AddTemplateOverloadCandidate(
template_decl, found_decl, ExplicitTemplateArgs, args,
candidate_set, SuppressUserConversions, PartialOverloading);
} else {
sema.AddOverloadCandidate(fn_decl, found_decl, args, candidate_set,
SuppressUserConversions, PartialOverloading);
}
}
}
}
auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
SemIR::CppOverloadSetId overload_set_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
-> SemIR::InstId {
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCallToCppFunction, Note,
"in call to Cpp function here");
builder.Note(loc_id, InCallToCppFunction);
});
// Map Carbon call argument types to C++ types.
clang::Expr* self_expr = nullptr;
if (self_id.has_value()) {
self_expr = InventClangArg(context, self_id);
if (!self_expr) {
return SemIR::ErrorInst::InstId;
}
}
auto arg_exprs = InventClangArgs(context, arg_ids);
if (!arg_exprs.has_value()) {
return SemIR::ErrorInst::InstId;
}
const SemIR::CppOverloadSet& overload_set =
context.cpp_overload_sets().Get(overload_set_id);
// Add candidate functions from the name lookup.
clang::OverloadCandidateSet candidate_set(
// TODO: Add location accordingly.
clang::SourceLocation(),
clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal);
clang::ASTUnit* ast = context.sem_ir().clang_ast_unit();
CARBON_CHECK(ast);
clang::Sema& sema = ast->getSema();
AddOverloadCandidataes(sema, candidate_set, overload_set.candidate_functions,
self_expr, *arg_exprs);
// Find best viable function among the candidates.
clang::OverloadCandidateSet::iterator best_viable_fn;
clang::OverloadingResult overloading_result =
// TODO: Add location accordingly.
candidate_set.BestViableFunction(sema, clang::SourceLocation(),
best_viable_fn);
switch (overloading_result) {
case clang::OverloadingResult::OR_Success: {
// TODO: Handle the cases when Function is null.
CARBON_CHECK(best_viable_fn->Function);
sema.MarkFunctionReferenced(clang::SourceLocation(),
best_viable_fn->Function);
SemIR::InstId result =
ImportCppFunctionDecl(context, loc_id, best_viable_fn->Function);
return result;
}
case clang::OverloadingResult::OR_No_Viable_Function: {
// TODO: Add notes with the candidates.
CARBON_DIAGNOSTIC(CppOverloadingNoViableFunctionFound, Error,
"no matching function for call to `{0}`",
SemIR::NameId);
context.emitter().Emit(loc_id, CppOverloadingNoViableFunctionFound,
overload_set.name_id);
return SemIR::ErrorInst::InstId;
}
case clang::OverloadingResult::OR_Ambiguous: {
// TODO: Add notes with the candidates.
CARBON_DIAGNOSTIC(CppOverloadingAmbiguousCandidatesFound, Error,
"call to `{0}` is ambiguous", SemIR::NameId);
context.emitter().Emit(loc_id, CppOverloadingAmbiguousCandidatesFound,
overload_set.name_id);
return SemIR::ErrorInst::InstId;
}
case clang::OverloadingResult::OR_Deleted: {
// TODO: Add notes with the candidates.
CARBON_DIAGNOSTIC(CppOverloadingDeletedFunctionFound, Error,
"call to deleted function `{0}`", SemIR::NameId);
context.emitter().Emit(loc_id, CppOverloadingDeletedFunctionFound,
overload_set.name_id);
return SemIR::ErrorInst::InstId;
}
}
}
} // namespace Carbon::Check