From b74e0d126045819bcacf9ee40f5227ccdaf00272 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 8 Apr 2026 12:29:25 -0700 Subject: [PATCH] Superficial support for exporting complete class types to C++. (#7029) We don't yet populate the bases or fields, so the class types show up as empty classes in C++ for now. But we do allow calls to static member functions. --- toolchain/check/cpp/export.cpp | 55 ++++++--- toolchain/check/cpp/generate_ast.cpp | 75 +++++++++++- .../testdata/interop/cpp/reverse/class.carbon | 109 ++++++++++++++++++ .../interop/cpp/reverse/function.carbon | 15 +-- .../testdata/interop/cpp/reverse/class.carbon | 84 ++++++++++++++ .../interop/cpp/reverse/function.carbon | 20 ++-- .../interop/cpp/reverse/method.carbon | 96 +++++++++++++++ 7 files changed, 417 insertions(+), 37 deletions(-) create mode 100644 toolchain/check/testdata/interop/cpp/reverse/class.carbon create mode 100644 toolchain/lower/testdata/interop/cpp/reverse/class.carbon create mode 100644 toolchain/lower/testdata/interop/cpp/reverse/method.carbon diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 9041f523c6b2..e28bddc105fb 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -21,7 +21,6 @@ namespace Carbon::Check { // The resulting decl is used to allow a generated C++ function to call // a generated Carbon function. static auto BuildCppFunctionDeclForCarbonFn(Context& context, - clang::DeclContext& decl_context, SemIR::LocId loc_id, SemIR::FunctionId function_id) -> clang::FunctionDecl* { @@ -57,11 +56,9 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context, function.name_id); auto* function_decl = clang::FunctionDecl::Create( - context.ast_context(), &decl_context, - /*StartLoc=*/clang_loc, - /*NLoc=*/clang_loc, clang::DeclarationName(identifier_info), - cpp_function_type, - /*TInfo=*/nullptr, clang::SC_Extern); + context.ast_context(), context.ast_context().getTranslationUnitDecl(), + /*StartLoc=*/clang_loc, /*NLoc=*/clang_loc, identifier_info, + cpp_function_type, /*TInfo=*/nullptr, clang::SC_Extern); // Build parameter decls. llvm::SmallVector param_var_decls; @@ -85,22 +82,42 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context, // Create the declaration of the C++ thunk. static auto BuildCppToCarbonThunkDecl( - Context& context, SemIR::LocId loc_id, clang::DeclarationName thunk_name, + Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context, + clang::DeclarationName thunk_name, llvm::ArrayRef thunk_param_types) -> clang::FunctionDecl* { clang::ASTContext& ast_context = context.ast_context(); auto clang_loc = GetCppLocation(context, loc_id); + clang::DeclarationNameInfo name_info(thunk_name, clang_loc); + auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo(); clang::QualType thunk_function_type = ast_context.getFunctionType( ast_context.VoidTy, thunk_param_types, ext_proto_info); - clang::DeclContext* decl_context = ast_context.getTranslationUnitDecl(); auto* tinfo = ast_context.getTrivialTypeSourceInfo(thunk_function_type, clang_loc); - clang::FunctionDecl* thunk_function_decl = clang::FunctionDecl::Create( - ast_context, decl_context, clang_loc, clang_loc, thunk_name, - thunk_function_type, tinfo, clang::SC_Static); + + const bool uses_fp_intrin = false; + const bool inline_specified = true; + const auto constexpr_kind = clang::ConstexprSpecKind::Unspecified; + const auto trailing_requires_clause = clang::AssociatedConstraint(); + + clang::FunctionDecl* thunk_function_decl = nullptr; + if (auto* parent_class = dyn_cast(decl_context)) { + // TODO: Support non-static methods. + thunk_function_decl = clang::CXXMethodDecl::Create( + ast_context, parent_class, clang_loc, name_info, thunk_function_type, + tinfo, clang::SC_Static, uses_fp_intrin, inline_specified, + constexpr_kind, clang_loc, trailing_requires_clause); + // TODO: Map Carbon access to C++ access. + thunk_function_decl->setAccess(clang::AS_public); + } else { + thunk_function_decl = clang::FunctionDecl::Create( + ast_context, decl_context, clang_loc, name_info, thunk_function_type, + tinfo, clang::SC_None, uses_fp_intrin, inline_specified, + /*hasWrittenPrototype=*/true, constexpr_kind, trailing_requires_clause); + } decl_context->addDecl(thunk_function_decl); llvm::SmallVector param_var_decls; @@ -113,9 +130,11 @@ static auto BuildCppToCarbonThunkDecl( } thunk_function_decl->setParams(param_var_decls); - // Set always_inline. + // Force the thunk to be inlined and discarded. thunk_function_decl->addAttr( clang::AlwaysInlineAttr::CreateImplicit(ast_context)); + thunk_function_decl->addAttr( + clang::InternalLinkageAttr::CreateImplicit(ast_context)); return thunk_function_decl; } @@ -151,8 +170,8 @@ static auto BuildCppToCarbonThunkBody(clang::Sema& sema, // with `MapToCppType`. (Note that the target function here is the // callee of the Carbon thunk.) static auto BuildCppToCarbonThunk( - Context& context, SemIR::LocId loc_id, llvm::StringRef base_name, - clang::FunctionDecl* carbon_function_decl, + Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context, + llvm::StringRef base_name, clang::FunctionDecl* carbon_function_decl, llvm::ArrayRef callee_param_type_ids) -> clang::FunctionDecl* { // Create the thunk's name. @@ -170,8 +189,8 @@ static auto BuildCppToCarbonThunk( param_types.push_back(cpp_type); } - auto* thunk_function_decl = - BuildCppToCarbonThunkDecl(context, loc_id, &thunk_ident, param_types); + auto* thunk_function_decl = BuildCppToCarbonThunkDecl( + context, loc_id, decl_context, &thunk_ident, param_types); // Build the thunk function body. clang::Sema& sema = context.clang_sema(); @@ -258,13 +277,13 @@ auto GetReverseInteropFunctionDecl(Context& context, SemIR::LocId loc_id, // Create a `clang::FunctionDecl` that can be used to call the Carbon thunk. auto* carbon_function_decl = BuildCppFunctionDeclForCarbonFn( - context, decl_context, loc_id, carbon_thunk_function_id); + context, loc_id, carbon_thunk_function_id); if (!carbon_function_decl) { return nullptr; } // Create a C++ thunk that calls the Carbon thunk. - return BuildCppToCarbonThunk(context, loc_id, + return BuildCppToCarbonThunk(context, loc_id, &decl_context, context.names().GetFormatted(callee.name_id), carbon_function_decl, callee_param_type_ids); } diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index 444e7fa3ae8e..1ed0bf6a44bc 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -32,6 +32,7 @@ #include "toolchain/check/cpp/import.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/name_lookup.h" +#include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/diagnostics/format_providers.h" @@ -324,14 +325,15 @@ class CarbonExternalASTSource : public clang::ExternalASTSource { clang::ASTContext* ast_context) : context_(context), ast_context_(ast_context) {} + auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override; + // Look up decls for `decl_name` inside `decl_context`, adding the decls to // `decl_context`. Returns true if any decls were added. auto FindExternalVisibleDeclsByName( const clang::DeclContext* decl_context, clang::DeclarationName decl_name, const clang::DeclContext* original_decl_context) -> bool override; - // See clang::ExternalASTSource. - auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override; + auto CompleteType(clang::TagDecl* tag_decl) -> void override; private: // Map a Carbon entity to a Clang NamedDecl. Returns null if the entity cannot @@ -339,6 +341,28 @@ class CarbonExternalASTSource : public clang::ExternalASTSource { auto MapInstIdToClangDecl(clang::DeclContext& decl_context, LookupResult lookup) -> clang::NamedDecl*; + // Get a current best-effort location for the current position within C++ + // processing. + auto GetCurrentCppLocId() -> SemIR::LocId { + auto* cpp_context = context_->cpp_context(); + CARBON_CHECK(cpp_context); + + // Use the current token location when parsing. + auto clang_source_loc = cpp_context->parser().getCurToken().getLocation(); + if (auto& code_synthesis_contexts = + cpp_context->sema().CodeSynthesisContexts; + !code_synthesis_contexts.empty()) { + // Use the current point of instantiation during template instantiation. + clang_source_loc = code_synthesis_contexts.back().PointOfInstantiation; + } + + // TODO: Refactor with AddImportIRInst in import.cpp. + SemIR::ClangSourceLocId clang_source_loc_id = + context_->sem_ir().clang_source_locs().Add(clang_source_loc); + return context_->import_ir_insts().Add( + SemIR::ImportIRInst(clang_source_loc_id)); + } + Check::Context* context_; clang::ASTContext* ast_context_; @@ -419,11 +443,19 @@ auto CarbonExternalASTSource::MapInstIdToClangDecl( GetClangIdentifierInfo(*context_, class_info.name_id); CARBON_CHECK(identifier_info, "class with non-identifier name {0}", class_info.name_id); + // TODO: Check whether we've already mapped this class and if so, return + // the prior mapping. auto* record_decl = clang::CXXRecordDecl::Create( *ast_context_, clang::TagTypeKind::Class, &decl_context, clang::SourceLocation(), clang::SourceLocation(), identifier_info); auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(record_decl); context_->clang_decls().Add({.key = key, .inst_id = target_inst_id}); + record_decl->setHasExternalLexicalStorage(); + record_decl->setHasExternalVisibleStorage(); + if (isa(decl_context)) { + // TODO: Map Carbon access to C++ access. + record_decl->setAccess(clang::AS_public); + } return record_decl; } case SemIR::StructValue::Kind: { @@ -536,6 +568,45 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName( return true; } +auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { + auto* class_decl = dyn_cast(tag_decl); + if (!class_decl) { + // TODO: If we start producing clang EnumTypes, we may have to handle them + // here too. + return; + } + + auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(tag_decl->getFirstDecl()); + auto clang_decl_id = context_->clang_decls().Lookup(key); + if (!clang_decl_id.has_value()) { + return; + } + + auto inst_id = context_->clang_decls().Get(clang_decl_id).inst_id; + auto const_id = context_->constant_values().Get(inst_id); + if (!const_id.has_value()) { + return; + } + + auto class_type = + context_->constant_values().TryGetInstAs(const_id); + if (!class_type) { + return; + } + + auto class_type_id = context_->types().GetTypeIdForTypeConstantId(const_id); + auto context_fn = [](DiagnosticContextBuilder& /*builder*/) -> void {}; + if (!RequireCompleteType(*context_, class_type_id, GetCurrentCppLocId(), + context_fn)) { + return; + } + + class_decl->startDefinition(); + // TODO: Import base class and fields, plus any special member functions that + // affect class properties. + class_decl->completeDefinition(); +} + // Parses a sequence of top-level declarations and forms a corresponding // representation in the Clang AST. Unlike clang::ParseAST, does not finish the // translation unit when EOF is reached. diff --git a/toolchain/check/testdata/interop/cpp/reverse/class.carbon b/toolchain/check/testdata/interop/cpp/reverse/class.carbon new file mode 100644 index 000000000000..45dc342ef4dc --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/reverse/class.carbon @@ -0,0 +1,109 @@ +// 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/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/reverse/class.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/reverse/class.carbon + +// --- static_members.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class A { + fn F() {} +} + +inline Cpp ''' +void G() { + Carbon::A::F(); +} +'''; + +// --- nested.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class A { + class B { + class C { + } + } +} + +inline Cpp ''' +void G() { + Carbon::A::B::C *p = nullptr; +} +'''; + +// --- fail_incomplete_concrete.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +// CHECK:STDERR: fail_incomplete_concrete.carbon:[[@LINE+4]]:1: error: class was forward declared here [ClassForwardDeclaredHere] +// CHECK:STDERR: class A; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +class A; + +inline Cpp ''' +// CHECK:STDERR: fail_incomplete_concrete.carbon:[[@LINE+4]]:11: error: variable has incomplete type 'Carbon::A' [CppInteropParseError] +// CHECK:STDERR: 16 | Carbon::A a; +// CHECK:STDERR: | ^ +// CHECK:STDERR: +Carbon::A a; +'''; + +// --- fail_todo_monomorphization_failure.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class B(N:! i32) { + var a: array(i8, N); +} + +// TODO: Once we allow this, we should produce an "invalid array bound" error +// during monomorphization triggered by the C++ code. +// CHECK:STDERR: fail_todo_monomorphization_failure.carbon:[[@LINE+4]]:11: error: alias initializer must be a name reference [AliasRequiresNameRef] +// CHECK:STDERR: alias T = B(-1); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +alias T = B(-1); + +inline Cpp ''' +// CHECK:STDERR: fail_todo_monomorphization_failure.carbon:[[@LINE+4]]:9: error: no type named 'T' in namespace 'Carbon' [CppInteropParseError] +// CHECK:STDERR: 22 | Carbon::T t; +// CHECK:STDERR: | ~~~~~~~~^ +// CHECK:STDERR: +Carbon::T t; +'''; + +// --- fail_todo_fields.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class A { + var a: i32; + var b: i32; +} + +inline Cpp ''' +// CHECK:STDERR: fail_todo_fields.carbon:[[@LINE+7]]:15: error: static assertion failed due to requirement 'sizeof(Carbon::A) == 2 * sizeof(int)' [CppInteropParseError] +// CHECK:STDERR: 18 | static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); +// CHECK:STDERR: | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_todo_fields.carbon:[[@LINE+4]]:33: note: expression evaluates to '1 == 8' [CppInteropParseNote] +// CHECK:STDERR: 18 | static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); +// CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); +'''; diff --git a/toolchain/check/testdata/interop/cpp/reverse/function.carbon b/toolchain/check/testdata/interop/cpp/reverse/function.carbon index 2ee447153924..7608a45525c7 100644 --- a/toolchain/check/testdata/interop/cpp/reverse/function.carbon +++ b/toolchain/check/testdata/interop/cpp/reverse/function.carbon @@ -114,16 +114,17 @@ void G() { library "[[@TEST_NAME]]"; +// CHECK:STDERR: fail_todo_method.carbon:[[@LINE+5]]:1: in import [InImport] +// CHECK:STDERR: other.carbon:11:3: error: semantics TODO: `unsupported: C++ calling a Carbon function with an implicit parameter` [SemanticsTodo] +// CHECK:STDERR: fn Method[self: Self]() { self; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: import Other; import Cpp inline ''' void G() { - // CHECK:STDERR: fail_todo_method.carbon:[[@LINE+8]]:3: error: invalid use of incomplete type 'Carbon::Other::C' [CppInteropParseError] - // CHECK:STDERR: 15 | Carbon::Other::C().Method(); - // CHECK:STDERR: | ^~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_todo_method.carbon:[[@LINE+4]]:21: error: member access into incomplete type 'Carbon::Other::C' [CppInteropParseError] - // CHECK:STDERR: 15 | Carbon::Other::C().Method(); - // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_todo_method.carbon:[[@LINE+4]]:22: error: no member named 'Method' in 'Carbon::Other::C' [CppInteropParseError] + // CHECK:STDERR: 16 | Carbon::Other::C().Method(); + // CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~ ^ // CHECK:STDERR: Carbon::Other::C().Method(); } diff --git a/toolchain/lower/testdata/interop/cpp/reverse/class.carbon b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon new file mode 100644 index 000000000000..9eddb7c5414a --- /dev/null +++ b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon @@ -0,0 +1,84 @@ +// 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/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/interop/cpp/reverse/class.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/reverse/class.carbon + +// --- pointer.carbon + +library "[[@TEST_NAME]]"; + +import Cpp; + +class A { + var x: i32; + var y: i32; +} + +inline Cpp ''' +Carbon::A *pass(Carbon::A *p) { return p; } +void call() { pass(nullptr); } +'''; + +fn DoIt() { Cpp.call(); } + +// CHECK:STDOUT: ; ModuleID = 'pointer.carbon' +// CHECK:STDOUT: source_filename = "pointer.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local noundef ptr @_Z4passPN6Carbon1AE(ptr noundef %p) #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %p.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %p, ptr %p.addr, align 8, !tbaa !11 +// CHECK:STDOUT: %0 = load ptr, ptr %p.addr, align 8, !tbaa !11 +// CHECK:STDOUT: ret ptr %0 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local void @_Z4callv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %call = call noundef ptr @_Z4passPN6Carbon1AE(ptr noundef null) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CDoIt.Main() #1 !dbg !14 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_Z4callv(), !dbg !17 +// CHECK:STDOUT: ret void, !dbg !18 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #1 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "pointer.carbon", directory: "") +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = !{!12, !12, i64 0} +// CHECK:STDOUT: !12 = !{!"p1 _ZTSN6Carbon1AE", !13, i64 0} +// CHECK:STDOUT: !13 = !{!"any pointer", !9, i64 0} +// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "DoIt", linkageName: "_CDoIt.Main", scope: null, file: !6, line: 16, type: !15, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !15 = !DISubroutineType(types: !16) +// CHECK:STDOUT: !16 = !{null} +// CHECK:STDOUT: !17 = !DILocation(line: 16, column: 13, scope: !14) +// CHECK:STDOUT: !18 = !DILocation(line: 16, column: 1, scope: !14) diff --git a/toolchain/lower/testdata/interop/cpp/reverse/function.carbon b/toolchain/lower/testdata/interop/cpp/reverse/function.carbon index 1f5331238d90..b1d320c983b0 100644 --- a/toolchain/lower/testdata/interop/cpp/reverse/function.carbon +++ b/toolchain/lower/testdata/interop/cpp/reverse/function.carbon @@ -124,22 +124,22 @@ fn H() { Cpp.G2(); } // CHECK:STDOUT: ; Function Attrs: mustprogress uwtable // CHECK:STDOUT: define dso_local void @_Z1Gv() #0 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @_ZL17NoArgs__cpp_thunkv() -// CHECK:STDOUT: call void @_ZL18BoolArg__cpp_thunkb(i1 noundef zeroext true) -// CHECK:STDOUT: call void @_ZL17IntArg__cpp_thunki(i32 noundef 123) -// CHECK:STDOUT: call void @_ZL19FloatArg__cpp_thunkf(float noundef 1.500000e+00) +// CHECK:STDOUT: call void @_ZN6Carbon5OtherL17NoArgs__cpp_thunkEv() +// CHECK:STDOUT: call void @_ZN6Carbon5OtherL18BoolArg__cpp_thunkEb(i1 noundef zeroext true) +// CHECK:STDOUT: call void @_ZN6Carbon5OtherL17IntArg__cpp_thunkEi(i32 noundef 123) +// CHECK:STDOUT: call void @_ZN6Carbon5OtherL19FloatArg__cpp_thunkEf(float noundef 1.500000e+00) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable -// CHECK:STDOUT: define internal void @_ZL17NoArgs__cpp_thunkv() #1 { +// CHECK:STDOUT: define internal void @_ZN6Carbon5OtherL17NoArgs__cpp_thunkEv() #1 { // CHECK:STDOUT: entry: // CHECK:STDOUT: call void @_CNoArgs__carbon_thunk.Other() // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable -// CHECK:STDOUT: define internal void @_ZL18BoolArg__cpp_thunkb(i1 noundef zeroext %0) #1 { +// CHECK:STDOUT: define internal void @_ZN6Carbon5OtherL18BoolArg__cpp_thunkEb(i1 noundef zeroext %0) #1 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.addr = alloca i8, align 1 // CHECK:STDOUT: %storedv = zext i1 %0 to i8 @@ -149,7 +149,7 @@ fn H() { Cpp.G2(); } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable -// CHECK:STDOUT: define internal void @_ZL17IntArg__cpp_thunki(i32 noundef %0) #1 { +// CHECK:STDOUT: define internal void @_ZN6Carbon5OtherL17IntArg__cpp_thunkEi(i32 noundef %0) #1 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.addr = alloca i32, align 4 // CHECK:STDOUT: store i32 %0, ptr %.addr, align 4, !tbaa !7 @@ -158,7 +158,7 @@ fn H() { Cpp.G2(); } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable -// CHECK:STDOUT: define internal void @_ZL19FloatArg__cpp_thunkf(float noundef %0) #1 { +// CHECK:STDOUT: define internal void @_ZN6Carbon5OtherL19FloatArg__cpp_thunkEf(float noundef %0) #1 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.addr = alloca float, align 4 // CHECK:STDOUT: store float %0, ptr %.addr, align 4, !tbaa !13 @@ -291,12 +291,12 @@ fn H() { Cpp.G2(); } // CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress uwtable // CHECK:STDOUT: define linkonce_odr dso_local void @_Z2G1v() #1 comdat { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @_ZL12F__cpp_thunkv() +// CHECK:STDOUT: call void @_ZN6CarbonL12F__cpp_thunkEv() // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable -// CHECK:STDOUT: define internal void @_ZL12F__cpp_thunkv() #2 { +// CHECK:STDOUT: define internal void @_ZN6CarbonL12F__cpp_thunkEv() #2 { // CHECK:STDOUT: entry: // CHECK:STDOUT: call void @_CF__carbon_thunk.Main() // CHECK:STDOUT: ret void diff --git a/toolchain/lower/testdata/interop/cpp/reverse/method.carbon b/toolchain/lower/testdata/interop/cpp/reverse/method.carbon new file mode 100644 index 000000000000..3a28ca54bf96 --- /dev/null +++ b/toolchain/lower/testdata/interop/cpp/reverse/method.carbon @@ -0,0 +1,96 @@ +// 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/none.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/interop/cpp/reverse/method.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/reverse/method.carbon + +// --- static.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class A { + fn F() {} +} + +inline Cpp ''' +void CallF() { + Carbon::A::F(); +} +'''; + +fn CallCallF() { Cpp.CallF(); } + +// CHECK:STDOUT: ; ModuleID = 'static.carbon' +// CHECK:STDOUT: source_filename = "static.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable +// CHECK:STDOUT: define dso_local void @_Z5CallFv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_ZN6Carbon1A12F__cpp_thunkEv() +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable +// CHECK:STDOUT: define internal void @_ZN6Carbon1A12F__cpp_thunkEv() #1 align 2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CF__carbon_thunk.A.Main() +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF.A.Main() #2 !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF__carbon_thunk.A.Main() #2 !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CF.A.Main(), !dbg !16 +// CHECK:STDOUT: ret void, !dbg !16 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CCallCallF.Main() #2 !dbg !17 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_Z5CallFv(), !dbg !18 +// CHECK:STDOUT: ret void, !dbg !19 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #2 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "static.carbon", directory: "") +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "F", linkageName: "_CF.A.Main", scope: null, file: !6, line: 6, type: !12, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{null} +// CHECK:STDOUT: !14 = !DILocation(line: 6, column: 3, scope: !11) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "F__carbon_thunk", linkageName: "_CF__carbon_thunk.A.Main", scope: null, file: !6, line: 6, type: !12, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !16 = !DILocation(line: 6, column: 3, scope: !15) +// CHECK:STDOUT: !17 = distinct !DISubprogram(name: "CallCallF", linkageName: "_CCallCallF.Main", scope: null, file: !6, line: 15, type: !12, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !18 = !DILocation(line: 15, column: 18, scope: !17) +// CHECK:STDOUT: !19 = !DILocation(line: 15, column: 1, scope: !17)