Skip to content

Commit 471b394

Browse files
authored
C++ interop: Support unary operators (#6020)
Newly supported: `-`. Partially supported due to lack of reference support: `++` (prefix), `--` (prefix). Not supported due to lack of Carbon support to call them correctly: `+`, `++` (postfix), `--` (postfix), `~`, `!`, `&`, `*`, `->`. Also (for consistency): * Add the operator declarations to unsupported binary operators tests. * Logical operators and the unary `operator&` (address of) are expected to be called by explicitly calling `operatorX`. 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_; }; auto operator++(MyNumber operand) -> MyNumber; auto operator--(MyNumber operand) -> MyNumber; auto operator-(MyNumber operand) -> MyNumber; ``` ```c++ // my_number.cpp #include "my_number.h" auto operator++(MyNumber operand) -> MyNumber { return MyNumber(operand.value() + 1); } auto operator--(MyNumber operand) -> MyNumber { return MyNumber(operand.value() - 1); } auto operator-(MyNumber operand) -> MyNumber { return MyNumber(-operand.value()); } ``` ```carbon // main.carbon library "Main"; import Core library "io"; import Cpp library "my_number.h"; fn Run() -> i32 { var num: Cpp.MyNumber = Cpp.MyNumber.MyNumber(14); Core.Print(num.value()); ++num; Core.Print(num.value()); --num; Core.Print(num.value()); num = -num; Core.Print(num.value()); return 0; } ``` ```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 14 14 -14 ``` Part of #5995.
1 parent 049abc6 commit 471b394

File tree

3 files changed

+363
-104
lines changed

3 files changed

+363
-104
lines changed

toolchain/check/import_cpp.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,6 +2076,31 @@ static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id,
20762076
llvm::StringLiteral interface_name,
20772077
llvm::StringLiteral op_name)
20782078
-> std::optional<clang::OverloadedOperatorKind> {
2079+
// Unary operators.
2080+
if (interface_name == "Destroy" || interface_name == "As" ||
2081+
interface_name == "ImplicitAs") {
2082+
// TODO: Support destructors and conversions.
2083+
return std::nullopt;
2084+
}
2085+
2086+
// Increment and Decrement.
2087+
if (interface_name == "Inc") {
2088+
CARBON_CHECK(op_name == "Op");
2089+
return clang::OO_PlusPlus;
2090+
}
2091+
if (interface_name == "Dec") {
2092+
CARBON_CHECK(op_name == "Op");
2093+
return clang::OO_MinusMinus;
2094+
}
2095+
2096+
// Arithmetic.
2097+
if (interface_name == "Negate") {
2098+
CARBON_CHECK(op_name == "Op");
2099+
return clang::OO_Minus;
2100+
}
2101+
2102+
// Binary operators.
2103+
20792104
// Arithmetic Operators.
20802105
if (interface_name == "AddWith") {
20812106
CARBON_CHECK(op_name == "Op");

toolchain/check/operator.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,40 @@ static auto GetOperatorOpFunction(Context& context, SemIR::LocId loc_id,
3535
op_name_id);
3636
}
3737

38+
// Returns whether the type of the instruction is a C++ class.
39+
static auto IsOfCppClassType(Context& context, SemIR::InstId inst_id) -> bool {
40+
auto class_type = context.insts().TryGetAs<SemIR::ClassType>(
41+
context.types().GetInstId(context.insts().Get(inst_id).type_id()));
42+
if (!class_type) {
43+
// Not a class.
44+
return false;
45+
}
46+
47+
const auto& class_info = context.classes().Get(class_type->class_id);
48+
if (!class_info.is_complete()) {
49+
return false;
50+
}
51+
52+
return context.name_scopes().Get(class_info.scope_id).is_cpp_scope();
53+
}
54+
3855
auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
3956
SemIR::InstId operand_id,
4057
MakeDiagnosticBuilderFn missing_impl_diagnoser)
4158
-> SemIR::InstId {
59+
// For unary operators with a C++ class as the operand, try to import and call
60+
// the C++ operator.
61+
// TODO: Change impl lookup instead. See
62+
// https://github.com/carbon-language/carbon-lang/blob/db0a00d713015436844c55e7ac190a0f95556499/toolchain/check/operator.cpp#L76
63+
if (IsOfCppClassType(context, operand_id)) {
64+
SemIR::ScopeLookupResult cpp_lookup_result =
65+
ImportOperatorFromCpp(context, loc_id, op);
66+
if (cpp_lookup_result.is_found()) {
67+
return PerformCall(context, loc_id, cpp_lookup_result.target_inst_id(),
68+
{operand_id});
69+
}
70+
}
71+
4272
// Look up the operator function.
4373
auto op_fn = GetOperatorOpFunction(context, loc_id, op);
4474

@@ -53,20 +83,6 @@ auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
5383
return PerformCall(context, loc_id, bound_op_id, {});
5484
}
5585

56-
// Returns whether the type of the instruction is a C++ class.
57-
static auto IsOfCppClassType(Context& context, SemIR::InstId inst_id) -> bool {
58-
auto class_type = context.insts().TryGetAs<SemIR::ClassType>(
59-
context.types().GetInstId(context.insts().Get(inst_id).type_id()));
60-
if (!class_type) {
61-
// Not a class.
62-
return false;
63-
}
64-
65-
return context.name_scopes()
66-
.Get(context.classes().Get(class_type->class_id).scope_id)
67-
.is_cpp_scope();
68-
}
69-
7086
auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
7187
SemIR::InstId lhs_id, SemIR::InstId rhs_id,
7288
MakeDiagnosticBuilderFn missing_impl_diagnoser)

0 commit comments

Comments
 (0)