mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 18:51:11 +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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user