From 471b394c6d8212866a22937df669e11824d5afba Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Mon, 8 Sep 2025 17:39:08 +0200 Subject: [PATCH] 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. --- toolchain/check/import_cpp.cpp | 25 ++ toolchain/check/operator.cpp | 44 +- .../interop/cpp/function/operators.carbon | 392 ++++++++++++++---- 3 files changed, 360 insertions(+), 101 deletions(-) diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index e86b3a148ec1..aab830aeca38 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -2076,6 +2076,31 @@ static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id, llvm::StringLiteral interface_name, llvm::StringLiteral op_name) -> std::optional { + // 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"); diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index ee692942c310..f7f03e23ba6b 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -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( + 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( - 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) diff --git a/toolchain/check/testdata/interop/cpp/function/operators.carbon b/toolchain/check/testdata/interop/cpp/function/operators.carbon index f21c1afab16f..25f376e686b1 100644 --- a/toolchain/check/testdata/interop/cpp/function/operators.carbon +++ b/toolchain/check/testdata/interop/cpp/function/operators.carbon @@ -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: // CHECK:STDOUT: } +// CHECK:STDOUT: %operator++__carbon_thunk.decl: %operator++__carbon_thunk.type = fn_decl @operator++__carbon_thunk [concrete = constants.%operator++__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %operator--__carbon_thunk.decl: %operator--__carbon_thunk.type = fn_decl @operator--__carbon_thunk [concrete = constants.%operator--__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %operator-__carbon_thunk.decl: %operator-__carbon_thunk.type = fn_decl @operator-__carbon_thunk [concrete = constants.%operator-__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// 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: = 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: = 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: = 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: = 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: = 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: = 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, [concrete = ] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: = 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 %.loc15_22.3, constants.%T.as.Destroy.impl.Op.21b // CHECK:STDOUT: -// CHECK:STDOUT: %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 %.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 %.loc12_3.3, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc12: = 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 %.loc11_3.3, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc11: = 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 %.loc8_26.3, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = 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: // CHECK:STDOUT: } // CHECK:STDOUT: