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:
Boaz Brickner
2025-09-19 16:42:46 +00:00
committed by GitHub
parent 05c9fd768e
commit ef488f00fa
10 changed files with 445 additions and 426 deletions
+5 -62
View File
@@ -8,63 +8,11 @@
#include "clang/Sema/Sema.h"
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/cpp/type_mapping.h"
#include "toolchain/sem_ir/expr_info.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
// Invents a Clang argument expression to use in overload resolution to
// represent the given Carbon argument instruction.
static auto InventClangArg(Context& context, SemIR::InstId arg_id)
-> clang::Expr* {
clang::ExprValueKind value_kind;
switch (SemIR::GetExprCategory(context.sem_ir(), arg_id)) {
case SemIR::ExprCategory::NotExpr:
CARBON_FATAL("Should not see these here");
case SemIR::ExprCategory::Error:
return nullptr;
case SemIR::ExprCategory::DurableRef:
value_kind = clang::ExprValueKind::VK_LValue;
break;
case SemIR::ExprCategory::EphemeralRef:
value_kind = clang::ExprValueKind::VK_XValue;
break;
case SemIR::ExprCategory::Value:
case SemIR::ExprCategory::Initializing:
value_kind = clang::ExprValueKind::VK_PRValue;
break;
case SemIR::ExprCategory::Mixed:
// TODO: Handle this by creating an InitListExpr.
value_kind = clang::ExprValueKind::VK_PRValue;
break;
}
if (context.insts().Get(arg_id).type_id() == SemIR::ErrorInst::TypeId) {
// The argument error has already been diagnosed.
return nullptr;
}
clang::QualType arg_cpp_type = MapToCppType(context, arg_id);
if (arg_cpp_type.isNull()) {
CARBON_DIAGNOSTIC(CppCallArgTypeNotSupported, Error,
"call argument of type {0} is not supported",
TypeOfInstId);
context.emitter().Emit(arg_id, CppCallArgTypeNotSupported, arg_id);
return nullptr;
}
// TODO: Avoid heap allocating more of these on every call. Either cache them
// somewhere or put them on the stack.
return new (context.ast_context()) clang::OpaqueValueExpr(
// TODO: Add location accordingly.
clang::SourceLocation(), arg_cpp_type.getNonReferenceType(), value_kind);
}
// Adds the given overload candidates to the candidate set.
static auto AddOverloadCandidataes(clang::Sema& sema,
clang::OverloadCandidateSet& candidate_set,
@@ -134,14 +82,9 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
return SemIR::ErrorInst::InstId;
}
}
llvm::SmallVector<clang::Expr*> arg_exprs;
arg_exprs.reserve(arg_ids.size());
for (SemIR::InstId arg_id : arg_ids) {
auto* arg_expr = InventClangArg(context, arg_id);
if (!arg_expr) {
return SemIR::ErrorInst::InstId;
}
arg_exprs.push_back(arg_expr);
auto arg_exprs = InventClangArgs(context, arg_ids);
if (!arg_exprs.has_value()) {
return SemIR::ErrorInst::InstId;
}
const SemIR::CppOverloadSet& overload_set =
@@ -158,7 +101,7 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
clang::Sema& sema = ast->getSema();
AddOverloadCandidataes(sema, candidate_set, overload_set.candidate_functions,
self_expr, arg_exprs);
self_expr, *arg_exprs);
// Find best viable function among the candidates.
clang::OverloadCandidateSet::iterator best_viable_fn;