mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:50:14 +01:00
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 https://github.com/carbon-language/carbon-lang/issues/5995.
This commit is contained in:
@@ -2076,6 +2076,31 @@ 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");
|
||||
|
||||
@@ -35,10 +35,40 @@ static auto GetOperatorOpFunction(Context& context, SemIR::LocId loc_id,
|
||||
op_name_id);
|
||||
}
|
||||
|
||||
// Returns whether the type of the instruction is a C++ class.
|
||||
static auto IsOfCppClassType(Context& context, SemIR::InstId inst_id) -> bool {
|
||||
auto class_type = context.insts().TryGetAs<SemIR::ClassType>(
|
||||
context.types().GetInstId(context.insts().Get(inst_id).type_id()));
|
||||
if (!class_type) {
|
||||
// Not a class.
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& class_info = context.classes().Get(class_type->class_id);
|
||||
if (!class_info.is_complete()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return context.name_scopes().Get(class_info.scope_id).is_cpp_scope();
|
||||
}
|
||||
|
||||
auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
|
||||
SemIR::InstId operand_id,
|
||||
MakeDiagnosticBuilderFn missing_impl_diagnoser)
|
||||
-> SemIR::InstId {
|
||||
// For unary operators with a C++ class as the operand, try to import and call
|
||||
// the C++ operator.
|
||||
// TODO: Change impl lookup instead. See
|
||||
// https://github.com/carbon-language/carbon-lang/blob/db0a00d713015436844c55e7ac190a0f95556499/toolchain/check/operator.cpp#L76
|
||||
if (IsOfCppClassType(context, operand_id)) {
|
||||
SemIR::ScopeLookupResult cpp_lookup_result =
|
||||
ImportOperatorFromCpp(context, loc_id, op);
|
||||
if (cpp_lookup_result.is_found()) {
|
||||
return PerformCall(context, loc_id, cpp_lookup_result.target_inst_id(),
|
||||
{operand_id});
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the operator function.
|
||||
auto op_fn = GetOperatorOpFunction(context, loc_id, op);
|
||||
|
||||
@@ -53,20 +83,6 @@ auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
|
||||
return PerformCall(context, loc_id, bound_op_id, {});
|
||||
}
|
||||
|
||||
// Returns whether the type of the instruction is a C++ class.
|
||||
static auto IsOfCppClassType(Context& context, SemIR::InstId inst_id) -> bool {
|
||||
auto class_type = context.insts().TryGetAs<SemIR::ClassType>(
|
||||
context.types().GetInstId(context.insts().Get(inst_id).type_id()));
|
||||
if (!class_type) {
|
||||
// Not a class.
|
||||
return false;
|
||||
}
|
||||
|
||||
return context.name_scopes()
|
||||
.Get(context.classes().Get(class_type->class_id).scope_id)
|
||||
.is_cpp_scope();
|
||||
}
|
||||
|
||||
auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
|
||||
SemIR::InstId lhs_id, SemIR::InstId rhs_id,
|
||||
MakeDiagnosticBuilderFn missing_impl_diagnoser)
|
||||
|
||||
+305
-87
@@ -12,31 +12,103 @@
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/operators.carbon
|
||||
|
||||
// ============================================================================
|
||||
// Negate
|
||||
// Unary operators
|
||||
// ============================================================================
|
||||
|
||||
// --- negate.h
|
||||
// --- unary_operators.h
|
||||
|
||||
class C {};
|
||||
|
||||
// Increment and Decrement.
|
||||
// TODO: Change `operand` and return value to reference when it is supported.
|
||||
auto operator++(C operand) -> C;
|
||||
auto operator--(C operand) -> C;
|
||||
|
||||
// Arithmetic.
|
||||
auto operator-(C operand) -> C;
|
||||
|
||||
// --- fail_todo_import_negate.carbon
|
||||
// --- import_unary_operators.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "negate.h";
|
||||
import Cpp library "unary_operators.h";
|
||||
|
||||
fn F() {
|
||||
//@dump-sem-ir-begin
|
||||
let c1: Cpp.C = Cpp.C.C();
|
||||
// CHECK:STDERR: fail_todo_import_negate.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.Negate` in type `Cpp.C` that does not implement that interface [MissingImplInMemberAccess]
|
||||
// CHECK:STDERR: let c2: Cpp.C = -c1;
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
let c2: Cpp.C = -c1;
|
||||
let c: Cpp.C = Cpp.C.C();
|
||||
|
||||
// Increment and Decrement.
|
||||
++c;
|
||||
--c;
|
||||
|
||||
// Arithmetic.
|
||||
let minus: Cpp.C = -c;
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Prefix increment and decrement vs postfix increment and decrement
|
||||
// ============================================================================
|
||||
|
||||
// --- prefix_inc_and_dec.h
|
||||
|
||||
class Prefix {};
|
||||
auto operator++(Prefix operand) -> Prefix;
|
||||
auto operator--(Prefix operand) -> Prefix;
|
||||
|
||||
// --- fail_prefix_calling_postfix.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "prefix_inc_and_dec.h";
|
||||
|
||||
fn F() {
|
||||
let prefix: Cpp.Prefix = Cpp.Prefix.Prefix();
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+8]]:9: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: prefix++;
|
||||
// CHECK:STDERR: ^~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: prefix++;
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
prefix++;
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+4]]:9: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: prefix--;
|
||||
// CHECK:STDERR: ^~
|
||||
// CHECK:STDERR:
|
||||
prefix--;
|
||||
}
|
||||
|
||||
// --- postfix_inc_and_dec.h
|
||||
|
||||
class Postfix {};
|
||||
auto operator++(Postfix operand, int) -> Postfix;
|
||||
auto operator--(Postfix operand, int) -> Postfix;
|
||||
|
||||
// --- fail_postfix_calling_prefix.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "postfix_inc_and_dec.h";
|
||||
|
||||
fn F() {
|
||||
let postfix: Cpp.Postfix = Cpp.Postfix.Postfix();
|
||||
// TODO: Make the error more descriptive to clarify we call prefix operators of a class with suffix operators.
|
||||
// CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+5]]:3: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch]
|
||||
// CHECK:STDERR: ++postfix;
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
// CHECK:STDERR: fail_postfix_calling_prefix.carbon: note: calling function declared here [InCallToEntity]
|
||||
// CHECK:STDERR:
|
||||
++postfix;
|
||||
// CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+5]]:3: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch]
|
||||
// CHECK:STDERR: --postfix;
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
// CHECK:STDERR: fail_postfix_calling_prefix.carbon: note: calling function declared here [InCallToEntity]
|
||||
// CHECK:STDERR:
|
||||
--postfix;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Binary operators
|
||||
// ============================================================================
|
||||
@@ -262,6 +334,99 @@ fn F() {
|
||||
let c: Cpp.C = s1 + s2;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Unsupported unary operators
|
||||
// ============================================================================
|
||||
|
||||
// --- unsupported_unary_operators.h
|
||||
|
||||
class C {};
|
||||
|
||||
// Arithmetic.
|
||||
auto operator+(C operand) -> C;
|
||||
|
||||
// Increment and Decrement.
|
||||
auto operator++(C operand, int) -> C;
|
||||
auto operator--(C operand, int) -> C;
|
||||
|
||||
// Bitwise.
|
||||
auto operator~(C operand) -> C;
|
||||
|
||||
// Logical.
|
||||
auto operator!(C operand) -> C;
|
||||
|
||||
// Pointer and Memory.
|
||||
class P {
|
||||
public:
|
||||
auto foo() -> P;
|
||||
auto operator->() const -> P* _Nonnull;
|
||||
};
|
||||
auto operator*(P operand) -> P;
|
||||
auto operator&(P operand) -> P;
|
||||
|
||||
// --- fail_todo_import_unsupported_unary_operators.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "unsupported_unary_operators.h";
|
||||
|
||||
fn F() {
|
||||
let c: Cpp.C = Cpp.C.C();
|
||||
|
||||
// Arithmetic.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+12]]:21: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: let plus: Cpp.C = +c;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+8]]:21: error: whitespace missing after binary operator [BinaryOperatorRequiresWhitespace]
|
||||
// CHECK:STDERR: let plus: Cpp.C = +c;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:21: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: let plus: Cpp.C = +c;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
let plus: Cpp.C = +c;
|
||||
|
||||
// Increment and Decrement.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:4: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: c++;
|
||||
// CHECK:STDERR: ^~
|
||||
// CHECK:STDERR:
|
||||
c++;
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:4: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: c--;
|
||||
// CHECK:STDERR: ^~
|
||||
// CHECK:STDERR:
|
||||
c--;
|
||||
|
||||
// Bitwise.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+8]]:26: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: let not_value: Cpp.C = ~c;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:26: error: `let` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: let not_value: Cpp.C = ~c;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
let not_value: Cpp.C = ~c;
|
||||
|
||||
// Logical.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:35: error: `let` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: let not_result: Cpp.C = operator!(c1);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
let not_result: Cpp.C = operator!(c1);
|
||||
|
||||
// Pointer and Memory.
|
||||
var p: Cpp.P = Cpp.P.P();
|
||||
|
||||
// Pointer and Memory.
|
||||
let dereference: Cpp.P = *p;
|
||||
let address: Cpp.P = operator&(p);
|
||||
let call_result: Cpp.P = p->foo();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Unsupported binary operators
|
||||
// ============================================================================
|
||||
@@ -270,6 +435,15 @@ fn F() {
|
||||
|
||||
class C {};
|
||||
|
||||
// Bitwise.
|
||||
// TODO: Change `lhs` and return value to reference when it is supported.
|
||||
auto operator<<=(C lhs, int num_bits) -> C;
|
||||
auto operator>>=(C lhs, int num_bits) -> C;
|
||||
|
||||
// Logical.
|
||||
auto operator&&(C lhs, C rhs) -> C;
|
||||
auto operator||(C lhs, C rhs) -> C;
|
||||
|
||||
// --- fail_todo_import_unsupported_binary_operators.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
@@ -278,6 +452,8 @@ import Cpp library "unsupported_binary_operators.h";
|
||||
|
||||
fn F() {
|
||||
let c1: Cpp.C = Cpp.C.C();
|
||||
|
||||
// Bitwise.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported operator interface `LeftShiftAssignWith`` [SemanticsTodo]
|
||||
// CHECK:STDERR: c1 <<= 1;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
@@ -302,64 +478,46 @@ fn F() {
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
c1 >>= 2;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Logical operators
|
||||
// ============================================================================
|
||||
|
||||
// --- logical_operators.h
|
||||
|
||||
class C {};
|
||||
|
||||
// --- fail_import_logical_operators.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "logical_operators.h";
|
||||
|
||||
fn F() {
|
||||
let c1: Cpp.C = Cpp.C.C();
|
||||
let c2: Cpp.C = Cpp.C.C();
|
||||
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+7]]:26: error: cannot implicitly convert expression of type `Cpp.C` to `bool` [ConversionFailure]
|
||||
// CHECK:STDERR: let not_result: bool = not c1;
|
||||
// CHECK:STDERR: ^~~~~~
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+4]]:26: note: type `Cpp.C` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
|
||||
// CHECK:STDERR: let not_result: bool = not c1;
|
||||
// CHECK:STDERR: ^~~~~~
|
||||
// Logical.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+16]]:27: error: name `operator` not found [NameNotFound]
|
||||
// CHECK:STDERR: let and_result: Cpp.C = operator&&(c1, c2);
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
let not_result: bool = not c1;
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+14]]:26: error: cannot implicitly convert expression of type `Cpp.C` to `bool` [ConversionFailure]
|
||||
// CHECK:STDERR: let and_result: bool = c1 and c2;
|
||||
// CHECK:STDERR: ^~~~~~
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+11]]:26: note: type `Cpp.C` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
|
||||
// CHECK:STDERR: let and_result: bool = c1 and c2;
|
||||
// CHECK:STDERR: ^~~~~~
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+12]]:35: error: whitespace missing around binary operator [BinaryOperatorRequiresWhitespace]
|
||||
// CHECK:STDERR: let and_result: Cpp.C = operator&&(c1, c2);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+7]]:26: error: cannot implicitly convert expression of type `Cpp.C` to `bool` [ConversionFailure]
|
||||
// CHECK:STDERR: let and_result: bool = c1 and c2;
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+4]]:26: note: type `Cpp.C` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
|
||||
// CHECK:STDERR: let and_result: bool = c1 and c2;
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+8]]:42: error: name `c2` not found [NameNotFound]
|
||||
// CHECK:STDERR: let and_result: Cpp.C = operator&&(c1, c2);
|
||||
// CHECK:STDERR: ^~
|
||||
// CHECK:STDERR:
|
||||
let and_result: bool = c1 and c2;
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+14]]:25: error: cannot implicitly convert expression of type `Cpp.C` to `bool` [ConversionFailure]
|
||||
// CHECK:STDERR: let or_result: bool = c1 or c2;
|
||||
// CHECK:STDERR: ^~~~~
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+11]]:25: note: type `Cpp.C` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
|
||||
// CHECK:STDERR: let or_result: bool = c1 or c2;
|
||||
// CHECK:STDERR: ^~~~~
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+4]]:36: error: cannot take the address of non-reference expression [AddrOfNonRef]
|
||||
// CHECK:STDERR: let and_result: Cpp.C = operator&&(c1, c2);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+7]]:25: error: cannot implicitly convert expression of type `Cpp.C` to `bool` [ConversionFailure]
|
||||
// CHECK:STDERR: let or_result: bool = c1 or c2;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR: fail_import_logical_operators.carbon:[[@LINE+4]]:25: note: type `Cpp.C` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
|
||||
// CHECK:STDERR: let or_result: bool = c1 or c2;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
let and_result: Cpp.C = operator&&(c1, c2);
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+20]]:26: error: name `operator` not found [NameNotFound]
|
||||
// CHECK:STDERR: let or_result: Cpp.C = operator||(c1, c2);
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
let or_result: bool = c1 or c2;
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+16]]:34: error: whitespace missing around binary operator [BinaryOperatorRequiresWhitespace]
|
||||
// CHECK:STDERR: let or_result: Cpp.C = operator||(c1, c2);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+12]]:35: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: let or_result: Cpp.C = operator||(c1, c2);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+8]]:35: error: whitespace missing around binary operator [BinaryOperatorRequiresWhitespace]
|
||||
// CHECK:STDERR: let or_result: Cpp.C = operator||(c1, c2);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_binary_operators.carbon:[[@LINE+4]]:35: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: let or_result: Cpp.C = operator||(c1, c2);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
let or_result: Cpp.C = operator||(c1, c2);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -475,7 +633,7 @@ fn F() {
|
||||
let c3: Cpp.C = c1 + c2;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_todo_import_negate.carbon
|
||||
// CHECK:STDOUT: --- import_unary_operators.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
@@ -486,6 +644,12 @@ fn F() {
|
||||
// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete]
|
||||
// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %operator++__carbon_thunk.type: type = fn_type @operator++__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %operator++__carbon_thunk: %operator++__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %operator--__carbon_thunk.type: type = fn_type @operator--__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %operator--__carbon_thunk: %operator--__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk.type: type = fn_type @operator-__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk: %operator-__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.1b3: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete]
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.21b: %T.as.Destroy.impl.Op.type.1b3 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
@@ -506,41 +670,95 @@ fn F() {
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %operator++__carbon_thunk.decl: %operator++__carbon_thunk.type = fn_decl @operator++__carbon_thunk [concrete = constants.%operator++__carbon_thunk] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %operator--__carbon_thunk.decl: %operator--__carbon_thunk.type = fn_decl @operator--__carbon_thunk [concrete = constants.%operator--__carbon_thunk] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk.decl: %operator-__carbon_thunk.type = fn_decl @operator-__carbon_thunk [concrete = constants.%operator-__carbon_thunk] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c1.patt: %pattern_type.217 = binding_pattern c1 [concrete]
|
||||
// CHECK:STDOUT: %c.patt: %pattern_type.217 = binding_pattern c [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Cpp.ref.loc8_19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc8_22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %C.ref.loc8_24: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C]
|
||||
// CHECK:STDOUT: %.loc8_27.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %addr.loc8_27.1: %ptr.d9e = addr_of %.loc8_27.1
|
||||
// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_27.1)
|
||||
// CHECK:STDOUT: %.loc8_27.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_27.1
|
||||
// CHECK:STDOUT: %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc8_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %Cpp.ref.loc8_18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc8_21: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %C.ref.loc8_23: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C]
|
||||
// CHECK:STDOUT: %.loc8_26.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %addr.loc8_26.1: %ptr.d9e = addr_of %.loc8_26.1
|
||||
// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_26.1)
|
||||
// CHECK:STDOUT: %.loc8_26.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_26.1
|
||||
// CHECK:STDOUT: %.loc8_13: type = splice_block %C.ref.loc8_13 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc8_10: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc8_13: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc8_27.3: ref %C = temporary %.loc8_27.1, %.loc8_27.2
|
||||
// CHECK:STDOUT: %.loc8_27.4: %C = bind_value %.loc8_27.3
|
||||
// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_27.4
|
||||
// CHECK:STDOUT: %.loc8_26.3: ref %C = temporary %.loc8_26.1, %.loc8_26.2
|
||||
// CHECK:STDOUT: %.loc8_26.4: %C = bind_value %.loc8_26.3
|
||||
// CHECK:STDOUT: %c: %C = bind_name c, %.loc8_26.4
|
||||
// CHECK:STDOUT: %c.ref.loc11: %C = name_ref c, %c
|
||||
// CHECK:STDOUT: %.loc11_3.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %.loc11_5: ref %C = value_as_ref %c.ref.loc11
|
||||
// CHECK:STDOUT: %addr.loc11_3.1: %ptr.d9e = addr_of %.loc11_5
|
||||
// CHECK:STDOUT: %addr.loc11_3.2: %ptr.d9e = addr_of %.loc11_3.1
|
||||
// CHECK:STDOUT: %operator++__carbon_thunk.call: init %empty_tuple.type = call imports.%operator++__carbon_thunk.decl(%addr.loc11_3.1, %addr.loc11_3.2)
|
||||
// CHECK:STDOUT: %.loc11_3.2: init %C = in_place_init %operator++__carbon_thunk.call, %.loc11_3.1
|
||||
// CHECK:STDOUT: %.loc11_3.3: ref %C = temporary %.loc11_3.1, %.loc11_3.2
|
||||
// CHECK:STDOUT: %c.ref.loc12: %C = name_ref c, %c
|
||||
// CHECK:STDOUT: %.loc12_3.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %.loc12_5: ref %C = value_as_ref %c.ref.loc12
|
||||
// CHECK:STDOUT: %addr.loc12_3.1: %ptr.d9e = addr_of %.loc12_5
|
||||
// CHECK:STDOUT: %addr.loc12_3.2: %ptr.d9e = addr_of %.loc12_3.1
|
||||
// CHECK:STDOUT: %operator--__carbon_thunk.call: init %empty_tuple.type = call imports.%operator--__carbon_thunk.decl(%addr.loc12_3.1, %addr.loc12_3.2)
|
||||
// CHECK:STDOUT: %.loc12_3.2: init %C = in_place_init %operator--__carbon_thunk.call, %.loc12_3.1
|
||||
// CHECK:STDOUT: %.loc12_3.3: ref %C = temporary %.loc12_3.1, %.loc12_3.2
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete]
|
||||
// CHECK:STDOUT: %minus.patt: %pattern_type.217 = binding_pattern minus [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %c1.ref: %C = name_ref c1, %c1
|
||||
// CHECK:STDOUT: %.loc13: type = splice_block %C.ref.loc13 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc13: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc13: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %c.ref.loc15: %C = name_ref c, %c
|
||||
// CHECK:STDOUT: %.loc15_22.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %.loc15_23: ref %C = value_as_ref %c.ref.loc15
|
||||
// CHECK:STDOUT: %addr.loc15_22.1: %ptr.d9e = addr_of %.loc15_23
|
||||
// CHECK:STDOUT: %addr.loc15_22.2: %ptr.d9e = addr_of %.loc15_22.1
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk.call: init %empty_tuple.type = call imports.%operator-__carbon_thunk.decl(%addr.loc15_22.1, %addr.loc15_22.2)
|
||||
// CHECK:STDOUT: %.loc15_22.2: init %C = in_place_init %operator-__carbon_thunk.call, %.loc15_22.1
|
||||
// CHECK:STDOUT: %.loc15_17: type = splice_block %C.ref.loc15 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc15: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc15: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %c2: %C = bind_name c2, <error> [concrete = <error>]
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_27.3, constants.%T.as.Destroy.impl.Op.21b
|
||||
// CHECK:STDOUT: %.loc15_22.3: ref %C = temporary %.loc15_22.1, %.loc15_22.2
|
||||
// CHECK:STDOUT: %.loc15_22.4: %C = bind_value %.loc15_22.3
|
||||
// CHECK:STDOUT: %minus: %C = bind_name minus, %.loc15_22.4
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc15: <bound method> = bound_method %.loc15_22.3, constants.%T.as.Destroy.impl.Op.21b
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %.loc8_27.3, %T.as.Destroy.impl.Op.specific_fn
|
||||
// CHECK:STDOUT: %addr.loc8_27.2: %ptr.d9e = addr_of %.loc8_27.3
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_27.2)
|
||||
// CHECK:STDOUT: %bound_method.loc15: <bound method> = bound_method %.loc15_22.3, %T.as.Destroy.impl.Op.specific_fn.1
|
||||
// CHECK:STDOUT: %addr.loc15_22.3: %ptr.d9e = addr_of %.loc15_22.3
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc15: init %empty_tuple.type = call %bound_method.loc15(%addr.loc15_22.3)
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc12: <bound method> = bound_method %.loc12_3.3, constants.%T.as.Destroy.impl.Op.21b
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %bound_method.loc12: <bound method> = bound_method %.loc12_3.3, %T.as.Destroy.impl.Op.specific_fn.2
|
||||
// CHECK:STDOUT: %addr.loc12_3.3: %ptr.d9e = addr_of %.loc12_3.3
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc12: init %empty_tuple.type = call %bound_method.loc12(%addr.loc12_3.3)
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc11: <bound method> = bound_method %.loc11_3.3, constants.%T.as.Destroy.impl.Op.21b
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %bound_method.loc11: <bound method> = bound_method %.loc11_3.3, %T.as.Destroy.impl.Op.specific_fn.3
|
||||
// CHECK:STDOUT: %addr.loc11_3.3: %ptr.d9e = addr_of %.loc11_3.3
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc11: init %empty_tuple.type = call %bound_method.loc11(%addr.loc11_3.3)
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_26.3, constants.%T.as.Destroy.impl.Op.21b
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %bound_method.loc8: <bound method> = bound_method %.loc8_26.3, %T.as.Destroy.impl.Op.specific_fn.4
|
||||
// CHECK:STDOUT: %addr.loc8_26.2: %ptr.d9e = addr_of %.loc8_26.3
|
||||
// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_26.2)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
Reference in New Issue
Block a user