From 870c5380a061d18f5872906d3d529e886c9d9058 Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Wed, 3 Sep 2025 14:01:20 +0200 Subject: [PATCH] C++ interop: Support importing binary `operator+` (#5996) Triggered by calling a binary operator with LHS being an imported C++ class type. Not supported (yet): * Multiple overloads. * Other operators. C++ Interop Demo: ```c++ // hello_world.h class C { public: C(int x) : x_(x) {} auto x() const -> int { return x_; } private: int x_ = 0; }; auto operator+ (C c1, C c2) -> C; ``` ```c++ // hello_world.cpp #include "hello_world.h" #include auto operator+ (C c1, C c2) -> C { printf("Adding %d with %d\n", c1.x(), c2.x()); return C(c1.x() + c2.x()); } ``` ```carbon // main.carbon library "Main"; import Cpp library "hello_world.h"; fn Run() -> i32 { let c1 : Cpp.C = Cpp.C.C(7); let c2 : Cpp.C = Cpp.C.C(8); let c3 : Cpp.C = c1 + c2; let c4 : Cpp.C = c3 + c2; return 0; } ``` ```shell $ clang -c hello_world.cpp $ bazel-bin/toolchain/carbon compile main.carbon $ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo $ ./demo Adding 7 with 8 Adding 15 with 8 ``` Part of #5995. --- toolchain/check/import_cpp.cpp | 207 +++-- toolchain/check/import_cpp.h | 6 + toolchain/check/operator.cpp | 32 + .../interop/cpp/function/operators.carbon | 873 ++++++++++++++++++ toolchain/diagnostics/diagnostic_kind.def | 1 + .../testdata/interop/cpp/fail_extern_c.carbon | 6 +- toolchain/sem_ir/ids.h | 4 +- toolchain/sem_ir/name.cpp | 2 + 8 files changed, 1066 insertions(+), 65 deletions(-) create mode 100644 toolchain/check/testdata/interop/cpp/function/operators.carbon diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index d63f08656edc..45ce8f73605f 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -37,6 +37,7 @@ #include "toolchain/check/import.h" #include "toolchain/check/inst.h" #include "toolchain/check/literal.h" +#include "toolchain/check/operator.h" #include "toolchain/check/pattern.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" @@ -485,18 +486,27 @@ auto ImportCppFiles(Context& context, return std::move(generated_ast); } -// Looks up the given name in the Clang AST in a specific scope. Returns the -// lookup result if lookup was successful. -static auto ClangLookupName(Context& context, SemIR::NameScopeId scope_id, - SemIR::NameId name_id) - -> std::optional { - std::optional name = - context.names().GetAsStringIfIdentifier(name_id); - if (!name) { - // Special names never exist in C++ code. - return std::nullopt; +// Returns the Clang `DeclContext` for the given name scope. Return the +// translation unit decl if no scope is provided. +static auto GetDeclContext(Context& context, SemIR::NameScopeId scope_id) + -> clang::DeclContext* { + if (!scope_id.has_value()) { + return context.ast_context().getTranslationUnitDecl(); } + auto scope_clang_decl_context_id = + context.name_scopes().Get(scope_id).clang_decl_context_id(); + return dyn_cast( + context.sem_ir().clang_decls().Get(scope_clang_decl_context_id).decl); +} +// Looks up the given declaration name in the Clang AST in a specific scope. +// Returns the found declaration and its access. If not found, returns +// `nullopt`. If there's not a single result, returns `nullptr` and default +// access. +static auto ClangLookupDeclarationName(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, + clang::DeclarationName name) + -> std::optional> { clang::ASTUnit* ast = context.sem_ir().clang_ast_unit(); CARBON_CHECK(ast); clang::Sema& sema = ast->getSema(); @@ -505,26 +515,36 @@ static auto ClangLookupName(Context& context, SemIR::NameScopeId scope_id, // here so that clang's diagnostics can point into the carbon code that uses // the name. clang::LookupResult lookup( - sema, - clang::DeclarationNameInfo( - clang::DeclarationName( - sema.getPreprocessor().getIdentifierInfo(*name)), - clang::SourceLocation()), + sema, clang::DeclarationNameInfo(name, clang::SourceLocation()), clang::Sema::LookupNameKind::LookupOrdinaryName); - auto scope_clang_decl_context_id = - context.name_scopes().Get(scope_id).clang_decl_context_id(); - bool found = sema.LookupQualifiedName( - lookup, dyn_cast(context.sem_ir() - .clang_decls() - .Get(scope_clang_decl_context_id) - .decl)); + bool found = + sema.LookupQualifiedName(lookup, GetDeclContext(context, scope_id)); if (!found) { return std::nullopt; } - return lookup; + std::tuple result{ + nullptr, clang::AccessSpecifier::AS_none}; + + // Access checks are performed separately by the Carbon name lookup logic. + lookup.suppressAccessDiagnostics(); + + if (!lookup.isSingleResult()) { + // Clang will diagnose ambiguous lookup results for us. + if (!lookup.isAmbiguous()) { + context.TODO(loc_id, + llvm::formatv("Unsupported: Lookup succeeded but couldn't " + "find a single result; LookupResultKind: {0}", + static_cast(lookup.getResultKind()))); + } + + return result; + } + + result = {lookup.getFoundDecl(), lookup.begin().getAccess()}; + return result; } // Looks up for constructors in the class scope and returns the lookup result. @@ -573,43 +593,48 @@ static auto IsDeclInjectedClassName(const Context& context, return true; } +// Returns a Clang DeclarationName for the given `NameId`. +static auto GetDeclarationName(Context& context, SemIR::NameId name_id) + -> std::optional { + std::optional name = + context.names().GetAsStringIfIdentifier(name_id); + if (!name) { + // Special names never exist in C++ code. + return std::nullopt; + } + + return clang::DeclarationName(context.sem_ir() + .clang_ast_unit() + ->getSema() + .getPreprocessor() + .getIdentifierInfo(*name)); +} + // Looks up the given name in the Clang AST in a specific scope, and returns the // found declaration and its access. If the found declaration is the injected // class name, looks up constructors instead. If not found, returns `nullopt`. // If there's not a single result, returns `nullptr` and default access. // Otherwise, returns the single declaration and its access. -static auto ClangLookup(Context& context, SemIR::LocId loc_id, - SemIR::NameScopeId scope_id, SemIR::NameId name_id) +static auto ClangLookupName(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> std::optional> { - auto lookup = ClangLookupName(context, scope_id, name_id); - if (!lookup) { + auto declaration_name = GetDeclarationName(context, name_id); + if (!declaration_name) { return std::nullopt; } - - std::tuple result{ - nullptr, clang::AccessSpecifier::AS_none}; - - // Access checks are performed separately by the Carbon name lookup logic. - lookup->suppressAccessDiagnostics(); - - if (!lookup->isSingleResult()) { - // Clang will diagnose ambiguous lookup results for us. - if (!lookup->isAmbiguous()) { - context.TODO(loc_id, - llvm::formatv("Unsupported: Lookup succeeded but couldn't " - "find a single result; LookupResultKind: {0}", - static_cast(lookup->getResultKind()))); - } - + auto result = + ClangLookupDeclarationName(context, loc_id, scope_id, *declaration_name); + if (!result) { return result; } - if (!IsDeclInjectedClassName(context, scope_id, name_id, - lookup->getFoundDecl())) { - result = {lookup->getFoundDecl(), lookup->begin().getAccess()}; + clang::NamedDecl* decl = std::get<0>(*result); + if (!decl || !IsDeclInjectedClassName(context, scope_id, name_id, decl)) { return result; } + result = {nullptr, clang::AccessSpecifier::AS_none}; + clang::DeclContextLookupResult constructors_lookup = ClangConstructorLookup(context, scope_id); @@ -1606,6 +1631,29 @@ static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id, .call_params_id = call_params_id}}; } +// Returns the Carbon function name for the given function. +static auto GetFunctionName(Context& context, clang::FunctionDecl* clang_decl) + -> SemIR::NameId { + switch (clang_decl->getDeclName().getNameKind()) { + case clang::DeclarationName::CXXConstructorName: { + return context.classes() + .Get(context.insts() + .GetAs(LookupClangDeclInstId( + context, cast(clang_decl->getParent()))) + .class_id) + .name_id; + } + + case clang::DeclarationName::CXXOperatorName: { + return SemIR::NameId::CppOperator; + } + + default: { + return AddIdentifierName(context, clang_decl->getName()); + } + } +} + // Creates a `FunctionDecl` and a `Function` without C++ thunk information. // Returns std::nullopt on failure. The given Clang declaration is assumed to: // * Have not been imported before. @@ -1634,19 +1682,8 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id, AddPlaceholderInstInNoBlock(context, Parse::NodeId::None, function_decl); context.imports().push_back(decl_id); - SemIR::NameId function_name_id = - isa(clang_decl) - ? context.classes() - .Get(context.insts() - .GetAs(LookupClangDeclInstId( - context, - cast(clang_decl->getParent()))) - .class_id) - .name_id - : AddIdentifierName(context, clang_decl->getName()); - auto function_info = SemIR::Function{ - {.name_id = function_name_id, + {.name_id = GetFunctionName(context, clang_decl), .parent_scope_id = GetParentNameScopeId(context, clang_decl), .generic_id = SemIR::GenericId::None, .first_param_node_id = Parse::NodeId::None, @@ -1965,7 +2002,7 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, builder.Note(loc_id, InCppNameLookup, name_id); }); - auto decl_and_access = ClangLookup(context, loc_id, scope_id, name_id); + auto decl_and_access = ClangLookupName(context, loc_id, scope_id, name_id); if (!decl_and_access) { return SemIR::ScopeLookupResult::MakeNotFound(); } @@ -1980,4 +2017,56 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, access); } +static auto GetOperatorKind(Context& context, SemIR::LocId loc_id, + llvm::StringLiteral interface_name) + -> std::optional { + if (interface_name == "AddWith") { + return clang::OO_Plus; + } + + context.TODO(loc_id, llvm::formatv("Unsupported operator interface `{0}`", + interface_name)); + return std::nullopt; +} + +auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id, Operator op) + -> SemIR::ScopeLookupResult { + Diagnostics::AnnotationScope annotate_diagnostics( + &context.emitter(), [&](auto& builder) { + CARBON_DIAGNOSTIC(InCppOperatorLookup, Note, + "in `Cpp` operator `{0}` lookup", std::string); + builder.Note(loc_id, InCppOperatorLookup, op.interface_name.str()); + }); + + auto op_kind = GetOperatorKind(context, loc_id, op.interface_name); + if (!op_kind) { + return SemIR::ScopeLookupResult::MakeNotFound(); + } + + // TODO: We should do ADL-only lookup for operators + // (`Sema::ArgumentDependentLookup`), when we support mapping Carbon types + // into C++ types. See + // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2316950123 + auto decl_and_access = ClangLookupDeclarationName( + context, loc_id, SemIR::NameScopeId::None, + context.ast_context().DeclarationNames.getCXXOperatorName(*op_kind)); + + if (!decl_and_access) { + return SemIR::ScopeLookupResult::MakeNotFound(); + } + auto [decl, access] = *decl_and_access; + if (!decl) { + return SemIR::ScopeLookupResult::MakeError(); + } + + SemIR::InstId inst_id = ImportDeclAndDependencies(context, loc_id, decl); + if (!inst_id.has_value()) { + return SemIR::ScopeLookupResult::MakeNotFound(); + } + + SemIR::AccessKind access_kind = MapAccess(access); + return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id, + access_kind); +} + } // namespace Carbon::Check diff --git a/toolchain/check/import_cpp.h b/toolchain/check/import_cpp.h index 7b97943e734a..901657e37589 100644 --- a/toolchain/check/import_cpp.h +++ b/toolchain/check/import_cpp.h @@ -11,6 +11,7 @@ #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/check/context.h" #include "toolchain/check/diagnostic_helpers.h" +#include "toolchain/check/operator.h" #include "toolchain/diagnostics/diagnostic_emitter.h" namespace Carbon::Check { @@ -31,6 +32,11 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::ScopeLookupResult; +// Looks up the given operator in the Clang AST generated when importing C++ +// code and returns a lookup result. +auto ImportOperatorFromCpp(Context& context, SemIR::LocId loc_id, Operator op) + -> SemIR::ScopeLookupResult; + // Given a Carbon class declaration that was imported from some kind of C++ // declaration, such as a class or enum, attempt to import a corresponding class // definition. Returns true if nothing went wrong (whether or not a definition diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index 083ad923d377..ee692942c310 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -7,6 +7,7 @@ #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/ids.h" @@ -52,10 +53,41 @@ 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) -> SemIR::InstId { + // For binary operators with a C++ class as at least one of the operands, try + // to import and call the C++ operator. + // TODO: Instead of hooking this here, change impl lookup, so that a generic + // constraint such as `T:! Core.Add` is satisfied by C++ class types that are + // addable. See + // 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)) { + 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(), + {lhs_id, rhs_id}); + } + } + // Look up the operator function. auto op_fn = GetOperatorOpFunction(context, loc_id, op); diff --git a/toolchain/check/testdata/interop/cpp/function/operators.carbon b/toolchain/check/testdata/interop/cpp/function/operators.carbon new file mode 100644 index 000000000000..cefa364419a7 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/function/operators.carbon @@ -0,0 +1,873 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon +// EXTRA-ARGS: --target=x86_64-linux-gnu +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/operators.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/operators.carbon + +// ============================================================================ +// Negate +// ============================================================================ + +// --- negate.h + +class C {}; +auto operator-(C operand) -> C; + +// --- fail_todo_import_negate.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "negate.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; + //@dump-sem-ir-end +} + +// ============================================================================ +// AddWith +// ============================================================================ + +// --- add_with.h + +class C {}; +auto operator+(C lhs, C rhs) -> C; + +// --- import_add_with.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "add_with.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.C = Cpp.C.C(); + let c2: Cpp.C = Cpp.C.C(); + let c3: Cpp.C = c1 + c2; + //@dump-sem-ir-end +} + +// --- multiple_calls.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "add_with.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.C = Cpp.C.C(); + let c2: Cpp.C = Cpp.C.C(); + let c3: Cpp.C = c1 + c2; + let c4: Cpp.C = c1 + c3; + let c5: Cpp.C = c4 + c3; + //@dump-sem-ir-end +} + +// --- fail_call_with_wrong_type.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "add_with.h"; + +fn F() { + let c1: Cpp.C = Cpp.C.C(); + // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+8]]:24: error: cannot implicitly convert expression of type `Core.IntLiteral` to `Cpp.C` [ConversionFailure] + // CHECK:STDERR: let c2: Cpp.C = c1 + 5; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+5]]:24: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(Cpp.C)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let c2: Cpp.C = c1 + 5; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_call_with_wrong_type.carbon: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: + let c2: Cpp.C = c1 + 5; + // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+8]]:19: error: cannot implicitly convert expression of type `Core.IntLiteral` to `Cpp.C` [ConversionFailure] + // CHECK:STDERR: let c3: Cpp.C = 6 + c1; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+5]]:19: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(Cpp.C)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let c3: Cpp.C = 6 + c1; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_call_with_wrong_type.carbon: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: + let c3: Cpp.C = 6 + c1; +} + +// ============================================================================ +// One of two operands conversion +// ============================================================================ + +// --- plus_with_int_conversion.h + +class C { + public: + C(int); +}; +auto operator+(C lhs, C rhs) -> C; + +// --- fail_todo_plus_with_int_conversion.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "plus_with_int_conversion.h"; + +fn F() { + let c1: Cpp.C = Cpp.C.C(4); + // CHECK:STDERR: fail_todo_plus_with_int_conversion.carbon:[[@LINE+8]]:24: error: cannot implicitly convert expression of type `Core.IntLiteral` to `Cpp.C` [ConversionFailure] + // CHECK:STDERR: let c2: Cpp.C = c1 + 5; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_todo_plus_with_int_conversion.carbon:[[@LINE+5]]:24: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(Cpp.C)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let c2: Cpp.C = c1 + 5; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_todo_plus_with_int_conversion.carbon: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: + let c2: Cpp.C = c1 + 5; + // CHECK:STDERR: fail_todo_plus_with_int_conversion.carbon:[[@LINE+8]]:19: error: cannot implicitly convert expression of type `Core.IntLiteral` to `Cpp.C` [ConversionFailure] + // CHECK:STDERR: let c3: Cpp.C = 6 + c1; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_todo_plus_with_int_conversion.carbon:[[@LINE+5]]:19: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(Cpp.C)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let c3: Cpp.C = 6 + c1; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_todo_plus_with_int_conversion.carbon: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: + let c3: Cpp.C = 6 + c1; +} + +// ============================================================================ +// All operands conversion +// ============================================================================ + +// --- plus_with_string_view_conversion.h + +namespace std { + using size_t = __SIZE_TYPE__; + + inline namespace __1 { + template struct char_traits {}; + + template> + class basic_string_view { + public: + basic_string_view() = default; + size_t size() const { return size_; } + + private: + const CharT* data_; + size_t size_; + }; + + using string_view = basic_string_view; + } +} + +class C { + public: + C(std::string_view s); +}; +auto operator+(C lhs, C rhs) -> C; + +// --- fail_todo_import_plus_with_string_view_conversion.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "plus_with_string_view_conversion.h"; + +fn F() { + let s1: str = "hello"; + let s2: str = "world"; + // CHECK:STDERR: fail_todo_import_plus_with_string_view_conversion.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.AddWith(Core.String)` in type `Core.String` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let c: Cpp.C = s1 + s2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + let c: Cpp.C = s1 + s2; +} + +// ============================================================================ +// SubWith +// ============================================================================ + +// --- sub_with.h + +class C {}; +auto operator-(C lhs, C rhs) -> C; + +// --- fail_todo_import_sub_with.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "sub_with.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.C = Cpp.C.C(); + let c2: Cpp.C = Cpp.C.C(); + // CHECK:STDERR: fail_todo_import_sub_with.carbon:[[@LINE+11]]:19: error: semantics TODO: `Unsupported operator interface `SubWith`` [SemanticsTodo] + // CHECK:STDERR: let c3: Cpp.C = c1 - c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_todo_import_sub_with.carbon:[[@LINE+8]]:19: note: in `Cpp` operator `SubWith` lookup [InCppOperatorLookup] + // CHECK:STDERR: let c3: Cpp.C = c1 - c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_import_sub_with.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.SubWith(Cpp.C)` in type `Cpp.C` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let c3: Cpp.C = c1 - c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + let c3: Cpp.C = c1 - c2; + //@dump-sem-ir-end +} + +// ============================================================================ +// Member operator +// ============================================================================ + +// --- member_add_with.h + +class C { + public: + auto operator+(C rhs) -> C; +}; + +// --- fail_todo_import_member__add_with.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "member_add_with.h"; + +fn F() { + //@dump-sem-ir-begin + let c1: Cpp.C = Cpp.C.C(); + let c2: Cpp.C = Cpp.C.C(); + // CHECK:STDERR: fail_todo_import_member__add_with.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.AddWith(Cpp.C)` in type `Cpp.C` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let c3: Cpp.C = c1 + c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + let c3: Cpp.C = c1 + c2; + //@dump-sem-ir-end +} + +// ============================================================================ +// Satisfying constraints +// ============================================================================ + +// --- fail_todo_constraints.carbon + +library "[[@TEST_NAME]]"; + +import Cpp inline ''' +class X {}; +X operator+(X, X); +'''; + +// TODO: Use Core.Add() when it is available. +// TODO: `Sum()` should be declared this way when generics implementation is ready for that: +// fn Sum[T:! Core.AddWith(.Self) where .Result = .Self](a: T, b: T) -> T +fn Sum[U:! type, T:! Core.AddWith(U) where .Result = U](a: T, b: U) -> U { + return a + b; +} +fn Call(x: Cpp.X) -> Cpp.X { + // CHECK:STDERR: fail_todo_constraints.carbon:[[@LINE+7]]:10: error: cannot convert type `Cpp.X` into type implementing `Core.AddWith(Cpp.X) where .(Core.AddWith(Cpp.X).Core.Result) = Cpp.X` [ConversionFailureTypeToFacet] + // CHECK:STDERR: return Sum(x, x); + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_todo_constraints.carbon:[[@LINE-7]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] + // CHECK:STDERR: fn Sum[U:! type, T:! Core.AddWith(U) where .Result = U](a: T, b: U) -> U { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return Sum(x, x); +} + +// ============================================================================ +// Operator not found +// ============================================================================ + +// --- not_found.h + +class C {}; + +// --- fail_import_not_found.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "not_found.h"; + +fn F() { + let c1: Cpp.C = Cpp.C.C(); + let c2: Cpp.C = Cpp.C.C(); + // CHECK:STDERR: fail_import_not_found.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.AddWith(Cpp.C)` in type `Cpp.C` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let c3: Cpp.C = c1 + c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + let c3: Cpp.C = c1 + c2; +} + +// ============================================================================ +// Operator overloading +// ============================================================================ + +// --- overloading.h + +class C {}; +auto operator+(C lhs, C rhs) -> C; +class D {}; +auto operator+(D lhs, D rhs) -> D; + +// --- fail_todo_import_overloading.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "overloading.h"; + +fn F() { + let c1: Cpp.C = Cpp.C.C(); + let c2: Cpp.C = Cpp.C.C(); + // CHECK:STDERR: fail_todo_import_overloading.carbon:[[@LINE+7]]:19: error: semantics TODO: `Unsupported: Lookup succeeded but couldn't find a single result; LookupResultKind: 3` [SemanticsTodo] + // CHECK:STDERR: let c3: Cpp.C = c1 + c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_todo_import_overloading.carbon:[[@LINE+4]]:19: note: in `Cpp` operator `AddWith` lookup [InCppOperatorLookup] + // CHECK:STDERR: let c3: Cpp.C = c1 + c2; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + let c3: Cpp.C = c1 + c2; +} + +// CHECK:STDOUT: --- fail_todo_import_negate.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.217: 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.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: %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: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [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.217 = binding_pattern c1 [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: } +// 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: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [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: } +// CHECK:STDOUT: %c2: %C = bind_name c2, [concrete = ] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: = bound_method %.loc8_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %.loc8_27.1, %T.as.Destroy.impl.Op.specific_fn +// CHECK:STDOUT: %addr.loc8_27.2: %ptr.d9e = addr_of %.loc8_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_27.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_add_with.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.217: 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.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: %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: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [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.217 = binding_pattern c1 [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.loc8: 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, %.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: } +// 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: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_24: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_27.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_27.1: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_27.1) +// CHECK:STDOUT: %.loc9_27.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_27.1 +// CHECK:STDOUT: %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_27.3: ref %C = temporary %.loc9_27.1, %.loc9_27.2 +// CHECK:STDOUT: %.loc9_27.4: %C = bind_value %.loc9_27.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_27.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.217 = 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_22.1: ref %C = temporary_storage +// CHECK:STDOUT: %.loc10_19: ref %C = value_as_ref %c1.ref +// CHECK:STDOUT: %addr.loc10_22.1: %ptr.d9e = addr_of %.loc10_19 +// CHECK:STDOUT: %.loc10_24: ref %C = value_as_ref %c2.ref +// CHECK:STDOUT: %addr.loc10_22.2: %ptr.d9e = addr_of %.loc10_24 +// CHECK:STDOUT: %addr.loc10_22.3: %ptr.d9e = addr_of %.loc10_22.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc10_22.1, %addr.loc10_22.2, %addr.loc10_22.3) +// CHECK:STDOUT: %.loc10_22.2: init %C = in_place_init %operator+__carbon_thunk.call, %.loc10_22.1 +// CHECK:STDOUT: %.loc10_14: type = splice_block %C.ref.loc10 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_22.3: ref %C = temporary %.loc10_22.1, %.loc10_22.2 +// CHECK:STDOUT: %.loc10_22.4: %C = bind_value %.loc10_22.3 +// CHECK:STDOUT: %c3: %C = bind_name c3, %.loc10_22.4 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_22.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_22.1, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc10_22.4: %ptr.d9e = addr_of %.loc10_22.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_22.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_27.1, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc9_27.2: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_27.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_27.1, %T.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc8_27.2: %ptr.d9e = addr_of %.loc8_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_27.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- multiple_calls.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.217: 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.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: %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: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [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.217 = binding_pattern c1 [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.loc8: 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, %.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: } +// 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: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_24: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_27.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_27.1: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_27.1) +// CHECK:STDOUT: %.loc9_27.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_27.1 +// CHECK:STDOUT: %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_27.3: ref %C = temporary %.loc9_27.1, %.loc9_27.2 +// CHECK:STDOUT: %.loc9_27.4: %C = bind_value %.loc9_27.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_27.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.217 = binding_pattern c3 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.ref.loc10: %C = name_ref c1, %c1 +// CHECK:STDOUT: %c2.ref: %C = name_ref c2, %c2 +// CHECK:STDOUT: %.loc10_22.1: ref %C = temporary_storage +// CHECK:STDOUT: %.loc10_19: ref %C = value_as_ref %c1.ref.loc10 +// CHECK:STDOUT: %addr.loc10_22.1: %ptr.d9e = addr_of %.loc10_19 +// CHECK:STDOUT: %.loc10_24: ref %C = value_as_ref %c2.ref +// CHECK:STDOUT: %addr.loc10_22.2: %ptr.d9e = addr_of %.loc10_24 +// CHECK:STDOUT: %addr.loc10_22.3: %ptr.d9e = addr_of %.loc10_22.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call.loc10: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc10_22.1, %addr.loc10_22.2, %addr.loc10_22.3) +// CHECK:STDOUT: %.loc10_22.2: init %C = in_place_init %operator+__carbon_thunk.call.loc10, %.loc10_22.1 +// CHECK:STDOUT: %.loc10_14: type = splice_block %C.ref.loc10 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_22.3: ref %C = temporary %.loc10_22.1, %.loc10_22.2 +// CHECK:STDOUT: %.loc10_22.4: %C = bind_value %.loc10_22.3 +// CHECK:STDOUT: %c3: %C = bind_name c3, %.loc10_22.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c4.patt: %pattern_type.217 = binding_pattern c4 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.ref.loc11: %C = name_ref c1, %c1 +// CHECK:STDOUT: %c3.ref.loc11: %C = name_ref c3, %c3 +// CHECK:STDOUT: %.loc11_22.1: ref %C = temporary_storage +// CHECK:STDOUT: %.loc11_19: ref %C = value_as_ref %c1.ref.loc11 +// CHECK:STDOUT: %addr.loc11_22.1: %ptr.d9e = addr_of %.loc11_19 +// CHECK:STDOUT: %.loc11_24: ref %C = value_as_ref %c3.ref.loc11 +// CHECK:STDOUT: %addr.loc11_22.2: %ptr.d9e = addr_of %.loc11_24 +// CHECK:STDOUT: %addr.loc11_22.3: %ptr.d9e = addr_of %.loc11_22.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call.loc11: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc11_22.1, %addr.loc11_22.2, %addr.loc11_22.3) +// CHECK:STDOUT: %.loc11_22.2: init %C = in_place_init %operator+__carbon_thunk.call.loc11, %.loc11_22.1 +// CHECK:STDOUT: %.loc11_14: type = splice_block %C.ref.loc11 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc11: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc11_22.3: ref %C = temporary %.loc11_22.1, %.loc11_22.2 +// CHECK:STDOUT: %.loc11_22.4: %C = bind_value %.loc11_22.3 +// CHECK:STDOUT: %c4: %C = bind_name c4, %.loc11_22.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c5.patt: %pattern_type.217 = binding_pattern c5 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c4.ref: %C = name_ref c4, %c4 +// CHECK:STDOUT: %c3.ref.loc12: %C = name_ref c3, %c3 +// CHECK:STDOUT: %.loc12_22.1: ref %C = temporary_storage +// CHECK:STDOUT: %.loc12_19: ref %C = value_as_ref %c4.ref +// CHECK:STDOUT: %addr.loc12_22.1: %ptr.d9e = addr_of %.loc12_19 +// CHECK:STDOUT: %.loc12_24: ref %C = value_as_ref %c3.ref.loc12 +// CHECK:STDOUT: %addr.loc12_22.2: %ptr.d9e = addr_of %.loc12_24 +// CHECK:STDOUT: %addr.loc12_22.3: %ptr.d9e = addr_of %.loc12_22.1 +// CHECK:STDOUT: %operator+__carbon_thunk.call.loc12: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc12_22.1, %addr.loc12_22.2, %addr.loc12_22.3) +// CHECK:STDOUT: %.loc12_22.2: init %C = in_place_init %operator+__carbon_thunk.call.loc12, %.loc12_22.1 +// CHECK:STDOUT: %.loc12_14: type = splice_block %C.ref.loc12 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc12: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc12_22.3: ref %C = temporary %.loc12_22.1, %.loc12_22.2 +// CHECK:STDOUT: %.loc12_22.4: %C = bind_value %.loc12_22.3 +// CHECK:STDOUT: %c5: %C = bind_name c5, %.loc12_22.4 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc12: = bound_method %.loc12_22.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc12: = bound_method %.loc12_22.1, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc12_22.4: %ptr.d9e = addr_of %.loc12_22.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc12: init %empty_tuple.type = call %bound_method.loc12(%addr.loc12_22.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc11: = bound_method %.loc11_22.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc11: = bound_method %.loc11_22.1, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc11_22.4: %ptr.d9e = addr_of %.loc11_22.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc11: init %empty_tuple.type = call %bound_method.loc11(%addr.loc11_22.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc10: = bound_method %.loc10_22.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc10: = bound_method %.loc10_22.1, %T.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc10_22.4: %ptr.d9e = addr_of %.loc10_22.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_22.4) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_27.1, %T.as.Destroy.impl.Op.specific_fn.4 +// CHECK:STDOUT: %addr.loc9_27.2: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_27.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_27.1, %T.as.Destroy.impl.Op.specific_fn.5 +// CHECK:STDOUT: %addr.loc8_27.2: %ptr.d9e = addr_of %.loc8_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_27.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_import_sub_with.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.217: 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.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: %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: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [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.217 = binding_pattern c1 [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.loc8: 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, %.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: } +// 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: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_24: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_27.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_27.1: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_27.1) +// CHECK:STDOUT: %.loc9_27.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_27.1 +// CHECK:STDOUT: %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_27.3: ref %C = temporary %.loc9_27.1, %.loc9_27.2 +// CHECK:STDOUT: %.loc9_27.4: %C = bind_value %.loc9_27.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_27.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.217 = 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: %.loc21: type = splice_block %C.ref.loc21 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc21: 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_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_27.1, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc9_27.2: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_27.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_27.1, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc8_27.2: %ptr.d9e = addr_of %.loc8_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_27.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_import_member__add_with.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.217: 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.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: %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: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [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.217 = binding_pattern c1 [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.loc8: 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, %.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: } +// 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: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_24: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] +// CHECK:STDOUT: %.loc9_27.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc9_27.1: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_27.1) +// CHECK:STDOUT: %.loc9_27.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_27.1 +// CHECK:STDOUT: %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_27.3: ref %C = temporary %.loc9_27.1, %.loc9_27.2 +// CHECK:STDOUT: %.loc9_27.4: %C = bind_value %.loc9_27.3 +// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_27.4 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.217 = 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: %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_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_27.1, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc9_27.2: %ptr.d9e = addr_of %.loc9_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_27.2) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: = bound_method %.loc8_27.1, constants.%T.as.Destroy.impl.Op.21b +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %.loc8_27.1, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc8_27.2: %ptr.d9e = addr_of %.loc8_27.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_27.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 3ae7cf297fac..3e81a0f6c3b4 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -363,6 +363,7 @@ CARBON_DIAGNOSTIC_KIND(QualifiedDeclInUndefinedInterfaceScope) // Name lookup. CARBON_DIAGNOSTIC_KIND(InCppNameLookup) +CARBON_DIAGNOSTIC_KIND(InCppOperatorLookup) CARBON_DIAGNOSTIC_KIND(InNameLookup) CARBON_DIAGNOSTIC_KIND(NameAmbiguousDueToExtend) CARBON_DIAGNOSTIC_KIND(NameNotFound) diff --git a/toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon b/toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon index 44d769110f04..5f6c3623ddea 100644 --- a/toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon +++ b/toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon @@ -50,16 +50,12 @@ struct X {}; extern "C" X operator+(X, X); -// --- fail_todo_import_extern_c_with_special_name.carbon +// --- import_extern_c_with_special_name.carbon library "[[@TEST_NAME]]"; import Cpp library "extern_c_with_special_name.h"; fn MyF(a: Cpp.X, b: Cpp.X) -> Cpp.X { - // CHECK:STDERR: fail_todo_import_extern_c_with_special_name.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.AddWith(Cpp.X)` in type `Cpp.X` that does not implement that interface [MissingImplInMemberAccess] - // CHECK:STDERR: return a + b; - // CHECK:STDERR: ^~~~~ - // CHECK:STDERR: return a + b; } diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index a0388b344a81..fa41fc66e188 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -577,7 +577,9 @@ constexpr FloatKind FloatKind::PPCFloat128 = FloatKind(6); /* The name of `_`. */ \ X(Underscore) \ /* The name of `vptr`. */ \ - X(Vptr) + X(Vptr) \ + /* The name of imported C++ operator functions */ \ + X(CppOperator) // The ID of a name. A name is either a string or a special name such as // `self`, `Self`, or `base`. diff --git a/toolchain/sem_ir/name.cpp b/toolchain/sem_ir/name.cpp index 27fc444fb72b..1c58cbf78664 100644 --- a/toolchain/sem_ir/name.cpp +++ b/toolchain/sem_ir/name.cpp @@ -39,6 +39,8 @@ static auto GetSpecialName(NameId name_id, bool for_ir) -> llvm::StringRef { return "_"; case NameId::SpecialNameId::Vptr: return for_ir ? "vptr" : ""; + case NameId::SpecialNameId::CppOperator: + return for_ir ? "cpp_operator" : ""; } }