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
+6 -226
View File
@@ -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)
+6 -7
View File
@@ -11,7 +11,6 @@
#include "llvm/Support/VirtualFileSystem.h"
#include "toolchain/check/context.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/check/operator.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
namespace Carbon::Check {
@@ -31,6 +30,12 @@ auto ImportCppFiles(Context& context,
auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
clang::FunctionDecl* clang_decl) -> SemIR::InstId;
// Imports an overloaded function set from Clang to Carbon.
auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,
SemIR::NameId name_id,
const clang::UnresolvedSet<4>& overload_set)
-> SemIR::InstId;
// Looks up the given name in the Clang AST generated when importing C++ code
// and returns a lookup result. If using the injected class name (`X.X()`),
// imports the class constructor as a function named as the class.
@@ -38,12 +43,6 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
-> SemIR::ScopeLookupResult;
// Looks up the given operator in the Clang AST generated when importing C++
// code and returns a lookup result.
auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id,
SemIR::NameScopeId scope_id, Operator op)
-> SemIR::ScopeLookupResult;
// Given a Carbon class declaration that was imported from some kind of C++
// declaration, such as a class or enum, attempt to import a corresponding class
// definition. Returns true if nothing went wrong (whether or not a definition
+197
View File
@@ -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
+22
View File
@@ -0,0 +1,22 @@
// 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
#ifndef CARBON_TOOLCHAIN_CHECK_CPP_OPERATORS_H_
#define CARBON_TOOLCHAIN_CHECK_CPP_OPERATORS_H_
#include "toolchain/check/context.h"
#include "toolchain/check/operator.h"
#include "toolchain/sem_ir/ids.h"
namespace Carbon::Check {
// Looks up the given operator in the Clang AST generated when importing C++
// code using argument dependent lookup (ADL) and return overload set
// instruction.
auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op,
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_CPP_OPERATORS_H_
+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;
+69 -1
View File
@@ -18,6 +18,7 @@
#include "toolchain/check/convert.h"
#include "toolchain/check/literal.h"
#include "toolchain/sem_ir/class.h"
#include "toolchain/sem_ir/expr_info.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/type.h"
@@ -177,8 +178,12 @@ static auto MapNonWrapperType(Context& context, SemIR::InstId inst_id,
}
}
// Maps a Carbon type to a C++ type. Accepts an InstId, representing a value
// whose type is mapped to a C++ type. Returns `clang::QualType` if the mapping
// succeeds, or `clang::QualType::isNull()` if the type is not supported.
// TODO: unify this with the C++ to Carbon type mapping function.
auto MapToCppType(Context& context, SemIR::InstId inst_id) -> clang::QualType {
static auto MapToCppType(Context& context, SemIR::InstId inst_id)
-> clang::QualType {
auto type_id = context.insts().Get(inst_id).type_id();
llvm::SmallVector<SemIR::TypeId> wrapper_types;
while (true) {
@@ -223,4 +228,67 @@ auto MapToCppType(Context& context, SemIR::InstId inst_id) -> clang::QualType {
return mapped_type;
}
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);
}
auto InventClangArgs(Context& context, llvm::ArrayRef<SemIR::InstId> arg_ids)
-> std::optional<llvm::SmallVector<clang::Expr*>> {
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 std::nullopt;
}
arg_exprs.push_back(arg_expr);
}
return arg_exprs;
}
} // namespace Carbon::Check
+9 -4
View File
@@ -11,10 +11,15 @@
namespace Carbon::Check {
// Maps a Carbon type to a C++ type. Accepts an InstId, representing a value
// whose type is mapped to a C++ type. Returns `clang::QualType` if the mapping
// succeeds, or `clang::QualType::isNull()` if the type is not supported.
auto MapToCppType(Context& context, SemIR::InstId inst_id) -> clang::QualType;
// Invents a Clang argument expression to use in overload resolution to
// represent the given Carbon argument instruction.
auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr*;
// For each arg, invents a Clang argument expression to use in overload
// resolution or argument dependent lookup (ADL) to represent the given Carbon
// argument instructions. Returns std::nullopt if any arg failed.
auto InventClangArgs(Context& context, llvm::ArrayRef<SemIR::InstId> arg_ids)
-> std::optional<llvm::SmallVector<clang::Expr*>>;
} // namespace Carbon::Check