From 0ea2dbf45601cce7163cad3e78682b93db036378 Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Mon, 8 Sep 2025 14:32:23 +0200 Subject: [PATCH 1/5] C++ interop: Support importing operators defined in namespaces. --- toolchain/check/import_cpp.cpp | 5 +- toolchain/check/import_cpp.h | 3 +- toolchain/check/operator.cpp | 32 +- .../interop/cpp/function/operators.carbon | 359 ++++++++++++++++++ 4 files changed, 388 insertions(+), 11 deletions(-) diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index e86b3a148ec16..25f7262573dc3 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -2186,7 +2186,8 @@ static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id, return std::nullopt; } -auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id, Operator op) +auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, Operator op) -> SemIR::ScopeLookupResult { Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { @@ -2206,7 +2207,7 @@ auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id, Operator op) // into C++ types. See // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2316950123 auto decl_and_access = ClangLookupDeclarationName( - context, loc_id, SemIR::NameScopeId::None, + context, loc_id, scope_id, context.ast_context().DeclarationNames.getCXXOperatorName(*op_kind)); if (!decl_and_access) { diff --git a/toolchain/check/import_cpp.h b/toolchain/check/import_cpp.h index 901657e37589f..4c4aeb8e507ba 100644 --- a/toolchain/check/import_cpp.h +++ b/toolchain/check/import_cpp.h @@ -34,7 +34,8 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, // 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, Operator op) +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++ diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index ee692942c3100..ef4367110169b 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -4,12 +4,15 @@ #include "toolchain/check/operator.h" +#include + #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/generic.h" #include "toolchain/check/import_cpp.h" #include "toolchain/check/member_access.h" #include "toolchain/check/name_lookup.h" +#include "toolchain/sem_ir/class.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" @@ -53,18 +56,24 @@ 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 { +// If the instruction is a C++ class, returns its parent scope id. Otherwise +// returns `std::nullopt`. +static auto GetCppClassTypeParentScope(Context& context, SemIR::InstId inst_id) + -> std::optional { 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 std::nullopt; + } + + const SemIR::Class& class_info = context.classes().Get(class_type->class_id); + if (!context.name_scopes().Get(class_info.scope_id).is_cpp_scope()) { + // Not a C++ class. + return std::nullopt; } - return context.name_scopes() - .Get(context.classes().Get(class_type->class_id).scope_id) - .is_cpp_scope(); + return class_info.parent_scope_id; } auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op, @@ -79,9 +88,16 @@ auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op, // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308666348 // and // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308664536 - if (IsOfCppClassType(context, lhs_id) || IsOfCppClassType(context, rhs_id)) { + llvm::SmallVector cpp_operand_parent_scope_ids; + for (SemIR::InstId operand_id : {lhs_id, rhs_id}) { + auto cpp_parent_scope_id = GetCppClassTypeParentScope(context, operand_id); + if (!cpp_parent_scope_id || llvm::is_contained(cpp_operand_parent_scope_ids, + *cpp_parent_scope_id)) { + continue; + } + cpp_operand_parent_scope_ids.push_back(*cpp_parent_scope_id); SemIR::ScopeLookupResult cpp_lookup_result = - ImportOperatorFromCpp(context, loc_id, op); + ImportOperatorFromCpp(context, loc_id, *cpp_parent_scope_id, op); if (cpp_lookup_result.is_found()) { return PerformCall(context, loc_id, cpp_lookup_result.target_inst_id(), {lhs_id, rhs_id}); diff --git a/toolchain/check/testdata/interop/cpp/function/operators.carbon b/toolchain/check/testdata/interop/cpp/function/operators.carbon index f21c1afab16fa..01de307296e4e 100644 --- a/toolchain/check/testdata/interop/cpp/function/operators.carbon +++ b/toolchain/check/testdata/interop/cpp/function/operators.carbon @@ -362,6 +362,61 @@ fn F() { let or_result: bool = c1 or c2; } +// ============================================================================ +// Operator and operands in a single namespace +// ============================================================================ + +// --- single_namespace.h + +namespace N { +class C {}; +auto operator+(C lhs, C rhs) -> C; +} + +// --- import_single_namespace.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "single_namespace.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.N.C = Cpp.N.C.C(); + let c2: Cpp.N.C = Cpp.N.C.C(); + let c3: Cpp.N.C = c1 + c2; + //@dump-sem-ir-end +} + +// ============================================================================ +// Operator and operands in a different namespaces +// ============================================================================ + +// --- multiple_namespaces.h + +namespace N1 { +class C1 {}; +} +namespace N2 { +class C2 {}; +auto operator+(N1::C1 lhs, C2 rhs) -> C2; +auto operator-(C2 lhs, N1::C1 rhs) -> C2; +} + +// --- import_multiple_namespaces.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "multiple_namespaces.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.N1.C1 = Cpp.N1.C1.C1(); + let c2: Cpp.N2.C2 = Cpp.N2.C2.C2(); + let c3: Cpp.N2.C2 = c1 + c2; + let c4: Cpp.N2.C2 = c2 - c1; + //@dump-sem-ir-end +} + // ============================================================================ // Member operator // ============================================================================ @@ -1547,6 +1602,310 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- import_single_namespace.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %pattern_type.69f: type = pattern_type %C [concrete] +// CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] +// CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.838: 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: %T.as.Destroy.impl.Op.type.9ae: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.dbb: %T.as.Destroy.impl.Op.type.9ae = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %N: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %C.C.decl: %C.C.type = fn_decl @C.C [concrete = constants.%C.C] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__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.69f = binding_pattern c1 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc8_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc8_24: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc8_26: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc8_28: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc8_31.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc8_31.1: %ptr.838 = addr_of %.loc8_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_31.1) +// CHECK:STDOUT: %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_31.1 +// CHECK:STDOUT: %.loc8_16: type = splice_block %C.ref.loc8_16 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc8_14: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc8_16: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_31.3: ref %C = temporary %.loc8_31.1, %.loc8_31.2 +// CHECK:STDOUT: %.loc8_31.4: %C = bind_value %.loc8_31.3 +// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_31.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.69f = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc9_24: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc9_26: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_28: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_31.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_31.1: %ptr.838 = addr_of %.loc9_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_31.1) +// CHECK:STDOUT: %.loc9_31.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_31.1 +// CHECK:STDOUT: %.loc9_16: type = splice_block %C.ref.loc9_16 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc9_14: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc9_16: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_31.3: ref %C = temporary %.loc9_31.1, %.loc9_31.2 +// CHECK:STDOUT: %.loc9_31.4: %C = bind_value %.loc9_31.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_31.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.69f = binding_pattern c3 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.ref: %C = name_ref c1, %c1 +// CHECK:STDOUT: %c2.ref: %C = name_ref c2, %c2 +// CHECK:STDOUT: %.loc10_24.1: ref %C = temporary_storage +// CHECK:STDOUT: %.loc10_21: ref %C = value_as_ref %c1.ref +// CHECK:STDOUT: %addr.loc10_24.1: %ptr.838 = addr_of %.loc10_21 +// CHECK:STDOUT: %.loc10_26: ref %C = value_as_ref %c2.ref +// CHECK:STDOUT: %addr.loc10_24.2: %ptr.838 = addr_of %.loc10_26 +// CHECK:STDOUT: %addr.loc10_24.3: %ptr.838 = addr_of %.loc10_24.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc10_24.1, %addr.loc10_24.2, %addr.loc10_24.3) +// CHECK:STDOUT: %.loc10_24.2: init %C = in_place_init %operator+__carbon_thunk.call, %.loc10_24.1 +// CHECK:STDOUT: %.loc10_16: type = splice_block %C.ref.loc10 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc10: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_24.3: ref %C = temporary %.loc10_24.1, %.loc10_24.2 +// CHECK:STDOUT: %.loc10_24.4: %C = bind_value %.loc10_24.3 +// CHECK:STDOUT: %c3: %C = bind_name c3, %.loc10_24.4 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_24.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_24.3, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc10_24.4: %ptr.838 = addr_of %.loc10_24.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_24.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_31.3, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc9_31.2: %ptr.838 = addr_of %.loc9_31.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_31.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_31.3, %T.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc8_31.2: %ptr.838 = addr_of %.loc8_31.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_31.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_multiple_namespaces.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %C1: type = class_type @C1 [concrete] +// CHECK:STDOUT: %pattern_type.20f: type = pattern_type %C1 [concrete] +// CHECK:STDOUT: %C1.C1.type: type = fn_type @C1.C1 [concrete] +// CHECK:STDOUT: %C1.C1: %C1.C1.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.087: type = ptr_type %C1 [concrete] +// CHECK:STDOUT: %C1__carbon_thunk.type: type = fn_type @C1__carbon_thunk [concrete] +// CHECK:STDOUT: %C1__carbon_thunk: %C1__carbon_thunk.type = struct_value () [concrete] +// CHECK:STDOUT: %C2: type = class_type @C2 [concrete] +// CHECK:STDOUT: %pattern_type.846: type = pattern_type %C2 [concrete] +// CHECK:STDOUT: %C2.C2.type: type = fn_type @C2.C2 [concrete] +// CHECK:STDOUT: %C2.C2: %C2.C2.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.51f: type = ptr_type %C2 [concrete] +// CHECK:STDOUT: %C2__carbon_thunk.type: type = fn_type @C2__carbon_thunk [concrete] +// CHECK:STDOUT: %C2__carbon_thunk: %C2__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.fbe: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C2) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.f22: %T.as.Destroy.impl.Op.type.fbe = struct_value () [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.e22: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C1) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.ba2: %T.as.Destroy.impl.Op.type.e22 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .N1 = %N1 +// CHECK:STDOUT: .N2 = %N2 +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %N1: = namespace [concrete] { +// CHECK:STDOUT: .C1 = %C1.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %C1.decl: type = class_decl @C1 [concrete = constants.%C1] {} {} +// CHECK:STDOUT: %C1.C1.decl: %C1.C1.type = fn_decl @C1.C1 [concrete = constants.%C1.C1] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %C1__carbon_thunk.decl: %C1__carbon_thunk.type = fn_decl @C1__carbon_thunk [concrete = constants.%C1__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %N2: = namespace [concrete] { +// CHECK:STDOUT: .C2 = %C2.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %C2.decl: type = class_decl @C2 [concrete = constants.%C2] {} {} +// CHECK:STDOUT: %C2.C2.decl: %C2.C2.type = fn_decl @C2.C2 [concrete = constants.%C2.C2] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %C2__carbon_thunk.decl: %C2__carbon_thunk.type = fn_decl @C2__carbon_thunk [concrete = constants.%C2__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.20f = binding_pattern c1 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc8_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N1.ref.loc8_26: = name_ref N1, imports.%N1 [concrete = imports.%N1] +// CHECK:STDOUT: %C1.ref.loc8_29: type = name_ref C1, imports.%C1.decl [concrete = constants.%C1] +// CHECK:STDOUT: %C1.ref.loc8_32: %C1.C1.type = name_ref C1, imports.%C1.C1.decl [concrete = constants.%C1.C1] +// CHECK:STDOUT: %.loc8_36.1: ref %C1 = temporary_storage +// CHECK:STDOUT: %addr.loc8_36.1: %ptr.087 = addr_of %.loc8_36.1 +// CHECK:STDOUT: %C1__carbon_thunk.call: init %empty_tuple.type = call imports.%C1__carbon_thunk.decl(%addr.loc8_36.1) +// CHECK:STDOUT: %.loc8_36.2: init %C1 = in_place_init %C1__carbon_thunk.call, %.loc8_36.1 +// CHECK:STDOUT: %.loc8_17: type = splice_block %C1.ref.loc8_17 [concrete = constants.%C1] { +// CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N1.ref.loc8_14: = name_ref N1, imports.%N1 [concrete = imports.%N1] +// CHECK:STDOUT: %C1.ref.loc8_17: type = name_ref C1, imports.%C1.decl [concrete = constants.%C1] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_36.3: ref %C1 = temporary %.loc8_36.1, %.loc8_36.2 +// CHECK:STDOUT: %.loc8_36.4: %C1 = bind_value %.loc8_36.3 +// CHECK:STDOUT: %c1: %C1 = bind_name c1, %.loc8_36.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.846 = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N2.ref.loc9_26: = name_ref N2, imports.%N2 [concrete = imports.%N2] +// CHECK:STDOUT: %C2.ref.loc9_29: type = name_ref C2, imports.%C2.decl [concrete = constants.%C2] +// CHECK:STDOUT: %C2.ref.loc9_32: %C2.C2.type = name_ref C2, imports.%C2.C2.decl [concrete = constants.%C2.C2] +// CHECK:STDOUT: %.loc9_36.1: ref %C2 = temporary_storage +// CHECK:STDOUT: %addr.loc9_36.1: %ptr.51f = addr_of %.loc9_36.1 +// CHECK:STDOUT: %C2__carbon_thunk.call: init %empty_tuple.type = call imports.%C2__carbon_thunk.decl(%addr.loc9_36.1) +// CHECK:STDOUT: %.loc9_36.2: init %C2 = in_place_init %C2__carbon_thunk.call, %.loc9_36.1 +// CHECK:STDOUT: %.loc9_17: type = splice_block %C2.ref.loc9_17 [concrete = constants.%C2] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N2.ref.loc9_14: = name_ref N2, imports.%N2 [concrete = imports.%N2] +// CHECK:STDOUT: %C2.ref.loc9_17: type = name_ref C2, imports.%C2.decl [concrete = constants.%C2] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_36.3: ref %C2 = temporary %.loc9_36.1, %.loc9_36.2 +// CHECK:STDOUT: %.loc9_36.4: %C2 = bind_value %.loc9_36.3 +// CHECK:STDOUT: %c2: %C2 = bind_name c2, %.loc9_36.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.846 = binding_pattern c3 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.ref.loc10: %C1 = name_ref c1, %c1 +// CHECK:STDOUT: %c2.ref.loc10: %C2 = name_ref c2, %c2 +// CHECK:STDOUT: %.loc10_26.1: ref %C2 = temporary_storage +// CHECK:STDOUT: %.loc10_23: ref %C1 = value_as_ref %c1.ref.loc10 +// CHECK:STDOUT: %addr.loc10_26.1: %ptr.087 = addr_of %.loc10_23 +// CHECK:STDOUT: %.loc10_28: ref %C2 = value_as_ref %c2.ref.loc10 +// CHECK:STDOUT: %addr.loc10_26.2: %ptr.51f = addr_of %.loc10_28 +// CHECK:STDOUT: %addr.loc10_26.3: %ptr.51f = addr_of %.loc10_26.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc10_26.1, %addr.loc10_26.2, %addr.loc10_26.3) +// CHECK:STDOUT: %.loc10_26.2: init %C2 = in_place_init %operator+__carbon_thunk.call, %.loc10_26.1 +// CHECK:STDOUT: %.loc10_17: type = splice_block %C2.ref.loc10 [concrete = constants.%C2] { +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N2.ref.loc10: = name_ref N2, imports.%N2 [concrete = imports.%N2] +// CHECK:STDOUT: %C2.ref.loc10: type = name_ref C2, imports.%C2.decl [concrete = constants.%C2] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_26.3: ref %C2 = temporary %.loc10_26.1, %.loc10_26.2 +// CHECK:STDOUT: %.loc10_26.4: %C2 = bind_value %.loc10_26.3 +// CHECK:STDOUT: %c3: %C2 = bind_name c3, %.loc10_26.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c4.patt: %pattern_type.846 = binding_pattern c4 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c2.ref.loc11: %C2 = name_ref c2, %c2 +// CHECK:STDOUT: %c1.ref.loc11: %C1 = name_ref c1, %c1 +// CHECK:STDOUT: %.loc11_26.1: ref %C2 = temporary_storage +// CHECK:STDOUT: %.loc11_23: ref %C2 = value_as_ref %c2.ref.loc11 +// CHECK:STDOUT: %addr.loc11_26.1: %ptr.51f = addr_of %.loc11_23 +// CHECK:STDOUT: %.loc11_28: ref %C1 = value_as_ref %c1.ref.loc11 +// CHECK:STDOUT: %addr.loc11_26.2: %ptr.087 = addr_of %.loc11_28 +// CHECK:STDOUT: %addr.loc11_26.3: %ptr.51f = addr_of %.loc11_26.1 +// CHECK:STDOUT: %operator-__carbon_thunk.call: init %empty_tuple.type = call imports.%operator-__carbon_thunk.decl(%addr.loc11_26.1, %addr.loc11_26.2, %addr.loc11_26.3) +// CHECK:STDOUT: %.loc11_26.2: init %C2 = in_place_init %operator-__carbon_thunk.call, %.loc11_26.1 +// CHECK:STDOUT: %.loc11_17: type = splice_block %C2.ref.loc11 [concrete = constants.%C2] { +// CHECK:STDOUT: %Cpp.ref.loc11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N2.ref.loc11: = name_ref N2, imports.%N2 [concrete = imports.%N2] +// CHECK:STDOUT: %C2.ref.loc11: type = name_ref C2, imports.%C2.decl [concrete = constants.%C2] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc11_26.3: ref %C2 = temporary %.loc11_26.1, %.loc11_26.2 +// CHECK:STDOUT: %.loc11_26.4: %C2 = bind_value %.loc11_26.3 +// CHECK:STDOUT: %c4: %C2 = bind_name c4, %.loc11_26.4 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc11: = bound_method %.loc11_26.3, constants.%T.as.Destroy.impl.Op.f22 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc11: = bound_method %.loc11_26.3, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc11_26.4: %ptr.51f = addr_of %.loc11_26.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc11: init %empty_tuple.type = call %bound_method.loc11(%addr.loc11_26.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_26.3, constants.%T.as.Destroy.impl.Op.f22 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_26.3, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc10_26.4: %ptr.51f = addr_of %.loc10_26.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_26.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_36.3, constants.%T.as.Destroy.impl.Op.f22 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_36.3, %T.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc9_36.2: %ptr.51f = addr_of %.loc9_36.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_36.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_36.3, constants.%T.as.Destroy.impl.Op.ba2 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_36.3, %T.as.Destroy.impl.Op.specific_fn.4 +// CHECK:STDOUT: %addr.loc8_36.2: %ptr.087 = addr_of %.loc8_36.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_36.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_import_member__add_with.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { From 2a7d984474055cda27dee4515dae1cae34c68b58 Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Tue, 9 Sep 2025 15:08:30 +0200 Subject: [PATCH 2/5] Support operators with inner classes. --- toolchain/check/operator.cpp | 13 +- .../interop/cpp/function/operators.carbon | 444 +++++++++++++++++- 2 files changed, 453 insertions(+), 4 deletions(-) diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index b40dcbf46c4a7..8d0bada7ecefa 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -14,6 +14,7 @@ #include "toolchain/check/name_lookup.h" #include "toolchain/sem_ir/class.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -56,7 +57,17 @@ static auto GetCppClassTypeParentScope(Context& context, SemIR::InstId inst_id) return std::nullopt; } - return class_info.parent_scope_id; + SemIR::NameScopeId parent_scope_id = class_info.parent_scope_id; + do { + SemIR::NameScope& scope = context.name_scopes().Get(parent_scope_id); + if (context.insts().Is(scope.inst_id())) { + break; + } + parent_scope_id = scope.parent_scope_id(); + + } while (parent_scope_id.has_value()); + + return parent_scope_id; } auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op, diff --git a/toolchain/check/testdata/interop/cpp/function/operators.carbon b/toolchain/check/testdata/interop/cpp/function/operators.carbon index 172684a1522db..a3729d712fe7a 100644 --- a/toolchain/check/testdata/interop/cpp/function/operators.carbon +++ b/toolchain/check/testdata/interop/cpp/function/operators.carbon @@ -529,7 +529,7 @@ fn F() { namespace N { class C {}; auto operator+(C lhs, C rhs) -> C; -} +} // namespace N // --- import_single_namespace.carbon @@ -553,12 +553,12 @@ fn F() { namespace N1 { class C1 {}; -} +} // namespace N1 namespace N2 { class C2 {}; auto operator+(N1::C1 lhs, C2 rhs) -> C2; auto operator-(C2 lhs, N1::C1 rhs) -> C2; -} +} // namespace N2 // --- import_multiple_namespaces.carbon @@ -575,6 +575,92 @@ fn F() { //@dump-sem-ir-end } +// ============================================================================ +// Operands in namespace, operator in global namespace +// ============================================================================ + +// --- operands_in_namespace_operator_in_global.h + +namespace N { +class C {}; +} // namespace N +auto operator+(N::C lhs, N::C rhs) -> N::C; + +void foo() { + N::C() + N::C(); +} + +// --- fail_todo_import_operands_in_namespace_operator_in_global.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "operands_in_namespace_operator_in_global.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.N.C = Cpp.N.C.C(); + let c2: Cpp.N.C = Cpp.N.C.C(); + // CHECK:STDERR: fail_todo_import_operands_in_namespace_operator_in_global.carbon:[[@LINE+4]]:21: error: cannot access member of interface `Core.AddWith(Cpp.N.C)` in type `Cpp.N.C` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let c3: Cpp.N.C = c1 + c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + let c3: Cpp.N.C = c1 + c2; + //@dump-sem-ir-end +} + +// ============================================================================ +// Operand is an inner class +// ============================================================================ + +// --- inner_class.h + +class O { + public: + class C {}; +}; +auto operator+(O::C lhs, O::C rhs) -> O::C; + +// --- import_inner_class.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "inner_class.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.O.C = Cpp.O.C.C(); + let c2: Cpp.O.C = Cpp.O.C.C(); + let c3: Cpp.O.C = c1 + c2; + //@dump-sem-ir-end +} + +// ============================================================================ +// Operand is an inner class in a namespace +// ============================================================================ + +// --- inner_class_in_namespace.h + +namespace N { +class O { + public: + class C {}; +}; +auto operator+(O::C lhs, O::C rhs) -> O::C; +} // namespace N + +// --- import_inner_class_in_namespace.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "inner_class_in_namespace.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.N.O.C = Cpp.N.O.C.C(); + let c2: Cpp.N.O.C = Cpp.N.O.C.C(); + let c3: Cpp.N.O.C = c1 + c2; + //@dump-sem-ir-end +} // ============================================================================ // Member operator // ============================================================================ @@ -2124,6 +2210,358 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_import_operands_in_namespace_operator_in_global.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %pattern_type.69f: type = pattern_type %C [concrete] +// CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] +// CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.838: 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: %T.as.Destroy.impl.Op.type.9ae: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.dbb: %T.as.Destroy.impl.Op.type.9ae = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %N: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %C.C.decl: %C.C.type = fn_decl @C.C [concrete = constants.%C.C] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__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.69f = binding_pattern c1 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc8_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc8_24: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc8_26: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc8_28: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc8_31.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc8_31.1: %ptr.838 = addr_of %.loc8_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_31.1) +// CHECK:STDOUT: %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_31.1 +// CHECK:STDOUT: %.loc8_16: type = splice_block %C.ref.loc8_16 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc8_14: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc8_16: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_31.3: ref %C = temporary %.loc8_31.1, %.loc8_31.2 +// CHECK:STDOUT: %.loc8_31.4: %C = bind_value %.loc8_31.3 +// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_31.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.69f = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc9_24: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc9_26: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_28: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_31.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_31.1: %ptr.838 = addr_of %.loc9_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_31.1) +// CHECK:STDOUT: %.loc9_31.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_31.1 +// CHECK:STDOUT: %.loc9_16: type = splice_block %C.ref.loc9_16 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc9_14: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc9_16: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_31.3: ref %C = temporary %.loc9_31.1, %.loc9_31.2 +// CHECK:STDOUT: %.loc9_31.4: %C = bind_value %.loc9_31.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_31.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.69f = binding_pattern c3 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.ref: %C = name_ref c1, %c1 +// CHECK:STDOUT: %c2.ref: %C = name_ref c2, %c2 +// CHECK:STDOUT: %.loc14: type = splice_block %C.ref.loc14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc14: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc14: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref.loc14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c3: %C = bind_name c3, [concrete = ] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_31.3, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc9_31.2: %ptr.838 = addr_of %.loc9_31.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_31.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_31.3, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc8_31.2: %ptr.838 = addr_of %.loc8_31.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_31.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_inner_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %O: type = class_type @O [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %pattern_type.b28: type = pattern_type %C [concrete] +// CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] +// CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.de2: 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: %T.as.Destroy.impl.Op.type.ac8: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.362: %T.as.Destroy.impl.Op.type.ac8 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .O = %O.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %O.decl: type = class_decl @O [concrete = constants.%O] {} {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %C.C.decl: %C.C.type = fn_decl @C.C [concrete = constants.%C.C] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__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.b28 = binding_pattern c1 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc8_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %O.ref.loc8_24: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc8_26: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc8_28: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc8_31.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc8_31.1: %ptr.de2 = addr_of %.loc8_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_31.1) +// CHECK:STDOUT: %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_31.1 +// CHECK:STDOUT: %.loc8_16: type = splice_block %C.ref.loc8_16 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %O.ref.loc8_14: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc8_16: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_31.3: ref %C = temporary %.loc8_31.1, %.loc8_31.2 +// CHECK:STDOUT: %.loc8_31.4: %C = bind_value %.loc8_31.3 +// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_31.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.b28 = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %O.ref.loc9_24: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc9_26: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_28: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_31.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_31.1: %ptr.de2 = addr_of %.loc9_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_31.1) +// CHECK:STDOUT: %.loc9_31.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_31.1 +// CHECK:STDOUT: %.loc9_16: type = splice_block %C.ref.loc9_16 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %O.ref.loc9_14: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc9_16: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_31.3: ref %C = temporary %.loc9_31.1, %.loc9_31.2 +// CHECK:STDOUT: %.loc9_31.4: %C = bind_value %.loc9_31.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_31.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.b28 = binding_pattern c3 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.ref: %C = name_ref c1, %c1 +// CHECK:STDOUT: %c2.ref: %C = name_ref c2, %c2 +// CHECK:STDOUT: %.loc10_24.1: ref %C = temporary_storage +// CHECK:STDOUT: %.loc10_21: ref %C = value_as_ref %c1.ref +// CHECK:STDOUT: %addr.loc10_24.1: %ptr.de2 = addr_of %.loc10_21 +// CHECK:STDOUT: %.loc10_26: ref %C = value_as_ref %c2.ref +// CHECK:STDOUT: %addr.loc10_24.2: %ptr.de2 = addr_of %.loc10_26 +// CHECK:STDOUT: %addr.loc10_24.3: %ptr.de2 = addr_of %.loc10_24.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc10_24.1, %addr.loc10_24.2, %addr.loc10_24.3) +// CHECK:STDOUT: %.loc10_24.2: init %C = in_place_init %operator+__carbon_thunk.call, %.loc10_24.1 +// CHECK:STDOUT: %.loc10_16: type = splice_block %C.ref.loc10 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %O.ref.loc10: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_24.3: ref %C = temporary %.loc10_24.1, %.loc10_24.2 +// CHECK:STDOUT: %.loc10_24.4: %C = bind_value %.loc10_24.3 +// CHECK:STDOUT: %c3: %C = bind_name c3, %.loc10_24.4 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_24.3, constants.%T.as.Destroy.impl.Op.362 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_24.3, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc10_24.4: %ptr.de2 = addr_of %.loc10_24.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_24.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_31.3, constants.%T.as.Destroy.impl.Op.362 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_31.3, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc9_31.2: %ptr.de2 = addr_of %.loc9_31.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_31.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_31.3, constants.%T.as.Destroy.impl.Op.362 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_31.3, %T.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc8_31.2: %ptr.de2 = addr_of %.loc8_31.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_31.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_inner_class_in_namespace.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %O: type = class_type @O [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %pattern_type.84b: type = pattern_type %C [concrete] +// CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] +// CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.4b2: 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: %T.as.Destroy.impl.Op.type.7ff: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.801: %T.as.Destroy.impl.Op.type.7ff = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %N: = namespace [concrete] { +// CHECK:STDOUT: .O = %O.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %O.decl: type = class_decl @O [concrete = constants.%O] {} {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %C.C.decl: %C.C.type = fn_decl @C.C [concrete = constants.%C.C] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__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.84b = binding_pattern c1 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc8_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc8_26: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %O.ref.loc8_28: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc8_30: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc8_32: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc8_35.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc8_35.1: %ptr.4b2 = addr_of %.loc8_35.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_35.1) +// CHECK:STDOUT: %.loc8_35.2: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_35.1 +// CHECK:STDOUT: %.loc8_18: type = splice_block %C.ref.loc8_18 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc8_14: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %O.ref.loc8_16: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc8_18: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_35.3: ref %C = temporary %.loc8_35.1, %.loc8_35.2 +// CHECK:STDOUT: %.loc8_35.4: %C = bind_value %.loc8_35.3 +// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_35.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.84b = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc9_26: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %O.ref.loc9_28: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc9_30: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_32: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_35.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_35.1: %ptr.4b2 = addr_of %.loc9_35.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_35.1) +// CHECK:STDOUT: %.loc9_35.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_35.1 +// CHECK:STDOUT: %.loc9_18: type = splice_block %C.ref.loc9_18 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc9_14: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %O.ref.loc9_16: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc9_18: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_35.3: ref %C = temporary %.loc9_35.1, %.loc9_35.2 +// CHECK:STDOUT: %.loc9_35.4: %C = bind_value %.loc9_35.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_35.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.84b = binding_pattern c3 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.ref: %C = name_ref c1, %c1 +// CHECK:STDOUT: %c2.ref: %C = name_ref c2, %c2 +// CHECK:STDOUT: %.loc10_26.1: ref %C = temporary_storage +// CHECK:STDOUT: %.loc10_23: ref %C = value_as_ref %c1.ref +// CHECK:STDOUT: %addr.loc10_26.1: %ptr.4b2 = addr_of %.loc10_23 +// CHECK:STDOUT: %.loc10_28: ref %C = value_as_ref %c2.ref +// CHECK:STDOUT: %addr.loc10_26.2: %ptr.4b2 = addr_of %.loc10_28 +// CHECK:STDOUT: %addr.loc10_26.3: %ptr.4b2 = addr_of %.loc10_26.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc10_26.1, %addr.loc10_26.2, %addr.loc10_26.3) +// CHECK:STDOUT: %.loc10_26.2: init %C = in_place_init %operator+__carbon_thunk.call, %.loc10_26.1 +// CHECK:STDOUT: %.loc10_18: type = splice_block %C.ref.loc10 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc10: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %O.ref.loc10: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_26.3: ref %C = temporary %.loc10_26.1, %.loc10_26.2 +// CHECK:STDOUT: %.loc10_26.4: %C = bind_value %.loc10_26.3 +// CHECK:STDOUT: %c3: %C = bind_name c3, %.loc10_26.4 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_26.3, constants.%T.as.Destroy.impl.Op.801 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_26.3, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc10_26.4: %ptr.4b2 = addr_of %.loc10_26.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_26.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_35.3, constants.%T.as.Destroy.impl.Op.801 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_35.3, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc9_35.2: %ptr.4b2 = addr_of %.loc9_35.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_35.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_35.3, constants.%T.as.Destroy.impl.Op.801 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_35.3, %T.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc8_35.2: %ptr.4b2 = addr_of %.loc8_35.3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_35.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_import_member__add_with.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { From bdda727c1287b15b0b0a6925ba5f129267012bc0 Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Wed, 10 Sep 2025 09:12:29 +0200 Subject: [PATCH 3/5] Add a TODO for ADL-only lookup. --- toolchain/check/operator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index 8d0bada7ecefa..f37a38b624c2b 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -78,6 +78,9 @@ auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op, // the C++ operator. // TODO: Change impl lookup instead. See // https://github.com/carbon-language/carbon-lang/blob/db0a00d713015436844c55e7ac190a0f95556499/toolchain/check/operator.cpp#L76 + // TODO: We should do ADL-only lookup for operators + // (`Sema::ArgumentDependentLookup`), when we support mapping Carbon types + // into C++ types. auto cpp_parent_scope_id = GetCppClassTypeParentScope(context, operand_id); if (cpp_parent_scope_id) { SemIR::ScopeLookupResult cpp_lookup_result = From 750d2a8231d873c2618ffbcf6ccd3e9adb7a26ff Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Wed, 10 Sep 2025 09:13:30 +0200 Subject: [PATCH 4/5] Add another TODO. --- toolchain/check/operator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index f37a38b624c2b..ee8fb4884e390 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -117,6 +117,9 @@ auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op, // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308666348 // and // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308664536 + // TODO: We should do ADL-only lookup for operators + // (`Sema::ArgumentDependentLookup`), when we support mapping Carbon types + // into C++ types. llvm::SmallVector cpp_operand_parent_scope_ids; for (SemIR::InstId operand_id : {lhs_id, rhs_id}) { auto cpp_parent_scope_id = GetCppClassTypeParentScope(context, operand_id); From 41e24b46b8281b72e1816f26c9da7fab41a0ffa9 Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Wed, 10 Sep 2025 09:41:05 +0200 Subject: [PATCH 5/5] Update tests following merge. --- .../interop/cpp/function/operators.carbon | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/toolchain/check/testdata/interop/cpp/function/operators.carbon b/toolchain/check/testdata/interop/cpp/function/operators.carbon index 080a893e97ad6..c7e83f5b37068 100644 --- a/toolchain/check/testdata/interop/cpp/function/operators.carbon +++ b/toolchain/check/testdata/interop/cpp/function/operators.carbon @@ -1919,8 +1919,8 @@ fn F() { // 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: %T.as.Destroy.impl.Op.type.9ae: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.dbb: %T.as.Destroy.impl.Op.type.9ae = struct_value () [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.054: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.2bd: %T.as.Destroy.impl.Op.type.054 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -2011,17 +2011,17 @@ fn F() { // CHECK:STDOUT: %.loc10_24.3: ref %C = temporary %.loc10_24.1, %.loc10_24.2 // CHECK:STDOUT: %.loc10_24.4: %C = bind_value %.loc10_24.3 // CHECK:STDOUT: %c3: %C = bind_name c3, %.loc10_24.4 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_24.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_24.3, constants.%T.as.Destroy.impl.Op.2bd // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_24.3, %T.as.Destroy.impl.Op.specific_fn.1 // CHECK:STDOUT: %addr.loc10_24.4: %ptr.838 = addr_of %.loc10_24.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_24.4) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_31.3, constants.%T.as.Destroy.impl.Op.2bd // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_31.3, %T.as.Destroy.impl.Op.specific_fn.2 // CHECK:STDOUT: %addr.loc9_31.2: %ptr.838 = addr_of %.loc9_31.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_31.2) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_31.3, constants.%T.as.Destroy.impl.Op.2bd // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_31.3, %T.as.Destroy.impl.Op.specific_fn.3 // CHECK:STDOUT: %addr.loc8_31.2: %ptr.838 = addr_of %.loc8_31.3 @@ -2051,10 +2051,10 @@ fn F() { // 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.fbe: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C2) [concrete] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.f22: %T.as.Destroy.impl.Op.type.fbe = struct_value () [concrete] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.e22: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C1) [concrete] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.ba2: %T.as.Destroy.impl.Op.type.e22 = struct_value () [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.65c: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C2) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.f25: %T.as.Destroy.impl.Op.type.65c = struct_value () [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.6cc: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C1) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.068: %T.as.Destroy.impl.Op.type.6cc = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -2187,22 +2187,22 @@ fn F() { // CHECK:STDOUT: %.loc11_26.3: ref %C2 = temporary %.loc11_26.1, %.loc11_26.2 // CHECK:STDOUT: %.loc11_26.4: %C2 = bind_value %.loc11_26.3 // CHECK:STDOUT: %c4: %C2 = bind_name c4, %.loc11_26.4 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc11: = bound_method %.loc11_26.3, constants.%T.as.Destroy.impl.Op.f22 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc11: = bound_method %.loc11_26.3, constants.%T.as.Destroy.impl.Op.f25 // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc11: = bound_method %.loc11_26.3, %T.as.Destroy.impl.Op.specific_fn.1 // CHECK:STDOUT: %addr.loc11_26.4: %ptr.51f = addr_of %.loc11_26.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc11: init %empty_tuple.type = call %bound_method.loc11(%addr.loc11_26.4) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_26.3, constants.%T.as.Destroy.impl.Op.f22 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_26.3, constants.%T.as.Destroy.impl.Op.f25 // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_26.3, %T.as.Destroy.impl.Op.specific_fn.2 // CHECK:STDOUT: %addr.loc10_26.4: %ptr.51f = addr_of %.loc10_26.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_26.4) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_36.3, constants.%T.as.Destroy.impl.Op.f22 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_36.3, constants.%T.as.Destroy.impl.Op.f25 // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_36.3, %T.as.Destroy.impl.Op.specific_fn.3 // CHECK:STDOUT: %addr.loc9_36.2: %ptr.51f = addr_of %.loc9_36.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_36.2) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_36.3, constants.%T.as.Destroy.impl.Op.ba2 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_36.3, constants.%T.as.Destroy.impl.Op.068 // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_36.3, %T.as.Destroy.impl.Op.specific_fn.4 // CHECK:STDOUT: %addr.loc8_36.2: %ptr.087 = addr_of %.loc8_36.3 @@ -2221,8 +2221,8 @@ fn F() { // CHECK:STDOUT: %ptr.838: 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: %T.as.Destroy.impl.Op.type.9ae: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.dbb: %T.as.Destroy.impl.Op.type.9ae = struct_value () [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.054: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.2bd: %T.as.Destroy.impl.Op.type.054 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -2298,12 +2298,12 @@ fn F() { // CHECK:STDOUT: %C.ref.loc14: type = name_ref C, imports.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %c3: %C = bind_name c3, [concrete = ] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_31.3, constants.%T.as.Destroy.impl.Op.2bd // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_31.3, %T.as.Destroy.impl.Op.specific_fn.1 // CHECK:STDOUT: %addr.loc9_31.2: %ptr.838 = addr_of %.loc9_31.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_31.2) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_31.3, constants.%T.as.Destroy.impl.Op.dbb +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_31.3, constants.%T.as.Destroy.impl.Op.2bd // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_31.3, %T.as.Destroy.impl.Op.specific_fn.2 // CHECK:STDOUT: %addr.loc8_31.2: %ptr.838 = addr_of %.loc8_31.3 @@ -2446,8 +2446,8 @@ fn F() { // 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: %T.as.Destroy.impl.Op.type.7ff: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] -// CHECK:STDOUT: %T.as.Destroy.impl.Op.801: %T.as.Destroy.impl.Op.type.7ff = struct_value () [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.c85: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.66d: %T.as.Destroy.impl.Op.type.c85 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -2544,17 +2544,17 @@ fn F() { // CHECK:STDOUT: %.loc10_26.3: ref %C = temporary %.loc10_26.1, %.loc10_26.2 // CHECK:STDOUT: %.loc10_26.4: %C = bind_value %.loc10_26.3 // CHECK:STDOUT: %c3: %C = bind_name c3, %.loc10_26.4 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_26.3, constants.%T.as.Destroy.impl.Op.801 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_26.3, constants.%T.as.Destroy.impl.Op.66d // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_26.3, %T.as.Destroy.impl.Op.specific_fn.1 // CHECK:STDOUT: %addr.loc10_26.4: %ptr.4b2 = addr_of %.loc10_26.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_26.4) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_35.3, constants.%T.as.Destroy.impl.Op.801 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_35.3, constants.%T.as.Destroy.impl.Op.66d // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_35.3, %T.as.Destroy.impl.Op.specific_fn.2 // CHECK:STDOUT: %addr.loc9_35.2: %ptr.4b2 = addr_of %.loc9_35.3 // CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_35.2) -// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_35.3, constants.%T.as.Destroy.impl.Op.801 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_35.3, constants.%T.as.Destroy.impl.Op.66d // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_35.3, %T.as.Destroy.impl.Op.specific_fn.3 // CHECK:STDOUT: %addr.loc8_35.2: %ptr.4b2 = addr_of %.loc8_35.3