From b88b53e7e31a483c421e3a5bf75569b816d24fd8 Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Mon, 8 Sep 2025 10:34:55 +0200 Subject: [PATCH] C++ interop: Add support for importing globals (#6005) This supports importing globals in the global scope and within namespaces, and support for class static data members. C++ Interop Demo: ```c++ // my_global.h extern int my_global; void inc_my_global(); ``` ```c++ // my_global.cpp #include "my_global.h" int my_global = 5; void inc_my_global() { ++my_global; } ``` ```carbon // main.carbon library "Main"; import Core library "io"; import Cpp library "my_global.h"; fn Run() -> i32 { Core.Print(Cpp.my_global); Cpp.inc_my_global(); Core.Print(Cpp.my_global); return 0; } ``` ```shell $ clang -c my_global.cpp $ bazel-bin/toolchain/carbon compile main.carbon $ bazel-bin/toolchain/carbon link my_global.o main.o --output=demo $ ./demo 5 6 ``` Part of #6006. --- toolchain/check/import_cpp.cpp | 57 ++++ .../basics/raw_sem_ir/multifile.carbon | 2 +- .../multifile_with_textual_ir.carbon | 2 +- .../basics/raw_sem_ir/one_file.carbon | 6 +- .../one_file_with_textual_ir.carbon | 2 +- .../testdata/interop/cpp/class/class.carbon | 34 +-- .../testdata/interop/cpp/class/struct.carbon | 32 +-- .../testdata/interop/cpp/class/union.carbon | 32 +-- .../check/testdata/interop/cpp/globals.carbon | 266 ++++++++++++++++++ toolchain/lower/mangler.cpp | 9 +- .../testdata/interop/cpp/extern_c.carbon | 119 +++++++- .../testdata/interop/cpp/fail_extern_c.carbon | 61 ---- .../lower/testdata/interop/cpp/globals.carbon | 57 ++++ toolchain/sem_ir/entity_name.h | 13 +- 14 files changed, 561 insertions(+), 131 deletions(-) create mode 100644 toolchain/check/testdata/interop/cpp/globals.carbon delete mode 100644 toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon create mode 100644 toolchain/lower/testdata/interop/cpp/globals.carbon diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index f33ec04378da..e86b3a148ec1 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -28,6 +28,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/class.h" #include "toolchain/check/context.h" +#include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/cpp_custom_type_mapping.h" #include "toolchain/check/cpp_thunk.h" @@ -1844,6 +1845,59 @@ static auto AddDependentUnimportedDecls(const Context& context, } } +static auto ImportVarDecl(Context& context, SemIR::LocId loc_id, + clang::VarDecl* var_decl) -> SemIR::InstId { + if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, var_decl); + existing_inst_id.has_value()) { + return existing_inst_id; + } + + // Extract type and name. + clang::QualType var_type = var_decl->getType(); + SemIR::TypeId var_type_id = MapType(context, loc_id, var_type).type_id; + if (!var_type_id.has_value()) { + context.TODO(loc_id, llvm::formatv("Unsupported: var type: {0}", + var_type.getAsString())); + return SemIR::ErrorInst::InstId; + } + SemIR::NameId var_name_id = AddIdentifierName(context, var_decl->getName()); + + SemIR::VarStorage var_storage{.type_id = var_type_id, + .pattern_id = SemIR::InstId::None}; + // We can't use the convenience for `AddPlaceholderInstInNoBlock()` with typed + // nodes because it doesn't support insts with cleanup. + SemIR::InstId var_storage_inst_id = + AddPlaceholderInstInNoBlock(context, {loc_id, var_storage}); + + auto clang_decl_id = context.sem_ir().clang_decls().Add( + {.decl = var_decl, .inst_id = var_storage_inst_id}); + + // Entity name referring to a Clang decl for mangling. + SemIR::EntityNameId entity_name_id = + context.entity_names().AddSymbolicBindingName( + var_name_id, GetParentNameScopeId(context, var_decl), + SemIR::CompileTimeBindIndex::None, false, clang_decl_id); + + // Create `BindingPattern` and `VarPattern` in a `NameBindingDecl`. + context.pattern_block_stack().Push(); + SemIR::TypeId pattern_type_id = GetPatternType(context, var_type_id); + SemIR::InstId binding_pattern_inst_id = AddPatternInst( + context, loc_id, + {.type_id = pattern_type_id, .entity_name_id = entity_name_id}); + var_storage.pattern_id = AddPatternInst( + context, Parse::VariablePatternId::None, + {.type_id = pattern_type_id, .subpattern_id = binding_pattern_inst_id}); + context.imports().push_back(AddInstInNoBlock( + context, loc_id, + {.pattern_block_id = context.pattern_block_stack().Pop()})); + + // Finalize the `VarStorage` instruction. + ReplaceInstBeforeConstantUse(context, var_storage_inst_id, var_storage); + context.imports().push_back(var_storage_inst_id); + + return var_storage_inst_id; +} + // Imports a declaration from Clang to Carbon. Returns the instruction for the // new Carbon declaration, which will be an ErrorInst on failure. Assumes all // dependencies have already been imported. @@ -1883,6 +1937,9 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id, if (auto* enum_const_decl = dyn_cast(clang_decl)) { return ImportEnumConstantDecl(context, enum_const_decl); } + if (auto* var_decl = dyn_cast(clang_decl)) { + return ImportVarDecl(context, loc_id, var_decl); + } context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()), llvm::formatv("Unsupported: Declaration type {0}", diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon index 7638fabe1428..45c1bfe036dd 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon @@ -97,7 +97,7 @@ fn B() { // CHECK:STDOUT: name_scope0: {inst: inst14, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name1: inst16, name0: inst17}} // CHECK:STDOUT: name_scope1: {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst22}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope1, index: -1, is_template: 0} +// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope1, index: -1, is_template: 0, clang_decl_id: clang_decl_id} // CHECK:STDOUT: functions: // CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, call_params_id: inst_block_empty, body: [inst_block5]} // CHECK:STDOUT: function1: {name: name1, parent_scope: name_scope1} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon index bd3a5a90d4d2..1994b772b72e 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon @@ -116,7 +116,7 @@ fn B() { // CHECK:STDOUT: name_scope0: {inst: inst14, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name1: inst16, name0: inst17}} // CHECK:STDOUT: name_scope1: {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst22}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope1, index: -1, is_template: 0} +// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope1, index: -1, is_template: 0, clang_decl_id: clang_decl_id} // CHECK:STDOUT: functions: // CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, call_params_id: inst_block_empty, body: [inst_block5]} // CHECK:STDOUT: function1: {name: name1, parent_scope: name_scope1} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon index 66d1b65944cb..14e8c787b0f2 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon @@ -27,9 +27,9 @@ fn Foo[T:! type](n: T) -> (T, ()) { // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: inst14, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name0: inst45}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name0: {name: name(PeriodSelf), parent_scope: name_scope, index: -1, is_template: 0} -// CHECK:STDOUT: entity_name1: {name: name1, parent_scope: name_scope, index: 0, is_template: 0} -// CHECK:STDOUT: entity_name2: {name: name2, parent_scope: name_scope, index: -1, is_template: 0} +// CHECK:STDOUT: entity_name0: {name: name(PeriodSelf), parent_scope: name_scope, index: -1, is_template: 0, clang_decl_id: clang_decl_id} +// CHECK:STDOUT: entity_name1: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, clang_decl_id: clang_decl_id} +// CHECK:STDOUT: entity_name2: {name: name2, parent_scope: name_scope, index: -1, is_template: 0, clang_decl_id: clang_decl_id} // CHECK:STDOUT: functions: // CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, call_params_id: inst_block13, return_slot_pattern: inst41, body: [inst_block20]} // CHECK:STDOUT: classes: {} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon index 48d898707e2c..c07a30318bf0 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon @@ -27,7 +27,7 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: inst14, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name0: inst37}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope, index: -1, is_template: 0} +// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, clang_decl_id: clang_decl_id} // CHECK:STDOUT: functions: // CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, call_params_id: inst_block9, return_slot_pattern: inst32, body: [inst_block12]} // CHECK:STDOUT: classes: {} diff --git a/toolchain/check/testdata/interop/cpp/class/class.carbon b/toolchain/check/testdata/interop/cpp/class/class.carbon index 691e846d4778..3cc2dd41347b 100644 --- a/toolchain/check/testdata/interop/cpp/class/class.carbon +++ b/toolchain/check/testdata/interop/cpp/class/class.carbon @@ -114,26 +114,18 @@ fn MyF() { class Bar { public: - static Bar* foo; + static Bar* _Nonnull foo; }; -// --- fail_todo_import_static_data_member.carbon +// --- import_static_data_member.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_import_static_data_member.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./static_data_member.h:4:15: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo] -// CHECK:STDERR: static Bar* foo; -// CHECK:STDERR: ^ import Cpp library "static_data_member.h"; fn MyF() { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_import_static_data_member.carbon:[[@LINE+4]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: let bar: Cpp.Bar* = Cpp.Bar.foo(); - // CHECK:STDERR: ^~~~~~~~~~~ - // CHECK:STDERR: - let bar: Cpp.Bar* = Cpp.Bar.foo(); + let bar: Cpp.Bar* = Cpp.Bar.foo; //@dump-sem-ir-end } @@ -367,7 +359,7 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_static_data_member.carbon +// CHECK:STDOUT: --- import_static_data_member.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bar: type = class_type @Bar [concrete] @@ -381,6 +373,7 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {} +// CHECK:STDOUT: %foo.var: ref %ptr.f68 = var %foo.var_patt // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MyF() { @@ -388,15 +381,16 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %bar.patt: %pattern_type = binding_pattern bar [concrete] // CHECK:STDOUT: } -// CHECK:STDOUT: %Cpp.ref.loc16_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref.loc16_26: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] -// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] -// CHECK:STDOUT: %.loc16: type = splice_block %ptr [concrete = constants.%ptr.f68] { -// CHECK:STDOUT: %Cpp.ref.loc16_12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref.loc16_15: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] -// CHECK:STDOUT: %ptr: type = ptr_type %Bar.ref.loc16_15 [concrete = constants.%ptr.f68] +// CHECK:STDOUT: %Cpp.ref.loc8_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref.loc8_26: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] +// CHECK:STDOUT: %foo.ref: ref %ptr.f68 = name_ref foo, imports.%foo.var +// CHECK:STDOUT: %.loc8_19: type = splice_block %ptr [concrete = constants.%ptr.f68] { +// CHECK:STDOUT: %Cpp.ref.loc8_12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref.loc8_15: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] +// CHECK:STDOUT: %ptr: type = ptr_type %Bar.ref.loc8_15 [concrete = constants.%ptr.f68] // CHECK:STDOUT: } -// CHECK:STDOUT: %bar: %ptr.f68 = bind_name bar, [concrete = ] +// CHECK:STDOUT: %.loc8_30: %ptr.f68 = bind_value %foo.ref +// CHECK:STDOUT: %bar: %ptr.f68 = bind_name bar, %.loc8_30 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/class/struct.carbon b/toolchain/check/testdata/interop/cpp/class/struct.carbon index f7c69b2a71e9..38de2a277e4e 100644 --- a/toolchain/check/testdata/interop/cpp/class/struct.carbon +++ b/toolchain/check/testdata/interop/cpp/class/struct.carbon @@ -115,23 +115,15 @@ struct Bar { static Bar* _Nonnull foo; }; -// --- fail_todo_import_static_data_member.carbon +// --- import_static_data_member.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_import_static_data_member.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./static_data_member.h:3:24: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo] -// CHECK:STDERR: static Bar* _Nonnull foo; -// CHECK:STDERR: ^ import Cpp library "static_data_member.h"; fn MyF() { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_import_static_data_member.carbon:[[@LINE+4]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: let bar: Cpp.Bar* = Cpp.Bar.foo(); - // CHECK:STDERR: ^~~~~~~~~~~ - // CHECK:STDERR: - let bar: Cpp.Bar* = Cpp.Bar.foo(); + let bar: Cpp.Bar* = Cpp.Bar.foo; //@dump-sem-ir-end } @@ -355,7 +347,7 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_static_data_member.carbon +// CHECK:STDOUT: --- import_static_data_member.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bar: type = class_type @Bar [concrete] @@ -369,6 +361,7 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {} +// CHECK:STDOUT: %foo.var: ref %ptr.f68 = var %foo.var_patt // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MyF() { @@ -376,15 +369,16 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %bar.patt: %pattern_type = binding_pattern bar [concrete] // CHECK:STDOUT: } -// CHECK:STDOUT: %Cpp.ref.loc16_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref.loc16_26: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] -// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] -// CHECK:STDOUT: %.loc16: type = splice_block %ptr [concrete = constants.%ptr.f68] { -// CHECK:STDOUT: %Cpp.ref.loc16_12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref.loc16_15: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] -// CHECK:STDOUT: %ptr: type = ptr_type %Bar.ref.loc16_15 [concrete = constants.%ptr.f68] +// CHECK:STDOUT: %Cpp.ref.loc8_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref.loc8_26: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] +// CHECK:STDOUT: %foo.ref: ref %ptr.f68 = name_ref foo, imports.%foo.var +// CHECK:STDOUT: %.loc8_19: type = splice_block %ptr [concrete = constants.%ptr.f68] { +// CHECK:STDOUT: %Cpp.ref.loc8_12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref.loc8_15: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] +// CHECK:STDOUT: %ptr: type = ptr_type %Bar.ref.loc8_15 [concrete = constants.%ptr.f68] // CHECK:STDOUT: } -// CHECK:STDOUT: %bar: %ptr.f68 = bind_name bar, [concrete = ] +// CHECK:STDOUT: %.loc8_30: %ptr.f68 = bind_value %foo.ref +// CHECK:STDOUT: %bar: %ptr.f68 = bind_name bar, %.loc8_30 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/class/union.carbon b/toolchain/check/testdata/interop/cpp/class/union.carbon index 7d9fd327b8f5..9043b464df5c 100644 --- a/toolchain/check/testdata/interop/cpp/class/union.carbon +++ b/toolchain/check/testdata/interop/cpp/class/union.carbon @@ -117,23 +117,15 @@ union Bar { static Bar* _Nonnull foo; }; -// --- fail_todo_import_static_data_member.carbon +// --- import_static_data_member.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_import_static_data_member.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./static_data_member.h:4:24: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo] -// CHECK:STDERR: static Bar* _Nonnull foo; -// CHECK:STDERR: ^ import Cpp library "static_data_member.h"; fn MyF() { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_import_static_data_member.carbon:[[@LINE+4]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: let bar: Cpp.Bar* = Cpp.Bar.foo(); - // CHECK:STDERR: ^~~~~~~~~~~ - // CHECK:STDERR: - let bar: Cpp.Bar* = Cpp.Bar.foo(); + let bar: Cpp.Bar* = Cpp.Bar.foo; //@dump-sem-ir-end } @@ -319,7 +311,7 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_static_data_member.carbon +// CHECK:STDOUT: --- import_static_data_member.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bar: type = class_type @Bar [concrete] @@ -333,6 +325,7 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {} +// CHECK:STDOUT: %foo.var: ref %ptr.f68 = var %foo.var_patt // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MyF() { @@ -340,15 +333,16 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %bar.patt: %pattern_type = binding_pattern bar [concrete] // CHECK:STDOUT: } -// CHECK:STDOUT: %Cpp.ref.loc16_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref.loc16_26: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] -// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] -// CHECK:STDOUT: %.loc16: type = splice_block %ptr [concrete = constants.%ptr.f68] { -// CHECK:STDOUT: %Cpp.ref.loc16_12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref.loc16_15: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] -// CHECK:STDOUT: %ptr: type = ptr_type %Bar.ref.loc16_15 [concrete = constants.%ptr.f68] +// CHECK:STDOUT: %Cpp.ref.loc8_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref.loc8_26: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] +// CHECK:STDOUT: %foo.ref: ref %ptr.f68 = name_ref foo, imports.%foo.var +// CHECK:STDOUT: %.loc8_19: type = splice_block %ptr [concrete = constants.%ptr.f68] { +// CHECK:STDOUT: %Cpp.ref.loc8_12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref.loc8_15: type = name_ref Bar, imports.%Bar.decl [concrete = constants.%Bar] +// CHECK:STDOUT: %ptr: type = ptr_type %Bar.ref.loc8_15 [concrete = constants.%ptr.f68] // CHECK:STDOUT: } -// CHECK:STDOUT: %bar: %ptr.f68 = bind_name bar, [concrete = ] +// CHECK:STDOUT: %.loc8_30: %ptr.f68 = bind_value %foo.ref +// CHECK:STDOUT: %bar: %ptr.f68 = bind_name bar, %.loc8_30 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/globals.carbon b/toolchain/check/testdata/interop/cpp/globals.carbon new file mode 100644 index 000000000000..f89bf31cd0c2 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/globals.carbon @@ -0,0 +1,266 @@ +// 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/destroy.carbon +// EXTRA-ARGS: --dump-sem-ir-ranges=if-present +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/globals.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/globals.carbon + +// ============================================================================ +// Global scope +// ============================================================================ + +// --- global_scope.h + +class C {}; +C global; + +// --- import_global_scope.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "global_scope.h"; + +fn MyF() { + let local: Cpp.C = Cpp.global; +} + +// ============================================================================ +// Namespace +// ============================================================================ + +// --- namespace.h + +namespace N { +class C {}; +C global; +} // namespace N + +// --- import_namespace.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "namespace.h"; + +fn MyF() { + let local: Cpp.N.C = Cpp.N.global; +} + +// ============================================================================ +// Unsupported global type +// ============================================================================ + +// --- unsupported_type.h + +using UnusupportedType = _BitInt(23); +UnusupportedType global; + +// --- fail_import_unsupported_type.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_import_unsupported_type.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./unsupported_type.h:2:7: error: semantics TODO: `Unsupported: Type declaration: UnusupportedType` [SemanticsTodo] +// CHECK:STDERR: using UnusupportedType = _BitInt(23); +// CHECK:STDERR: ^ +import Cpp library "unsupported_type.h"; + +fn MyF() { + // CHECK:STDERR: fail_import_unsupported_type.carbon:[[@LINE+11]]:14: note: in `Cpp` name lookup for `UnusupportedType` [InCppNameLookup] + // CHECK:STDERR: let local: Cpp.UnusupportedType = Cpp.global; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_import_unsupported_type.carbon:[[@LINE+7]]:37: error: semantics TODO: `Unsupported: var type: UnusupportedType` [SemanticsTodo] + // CHECK:STDERR: let local: Cpp.UnusupportedType = Cpp.global; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: fail_import_unsupported_type.carbon:[[@LINE+4]]:37: note: in `Cpp` name lookup for `global` [InCppNameLookup] + // CHECK:STDERR: let local: Cpp.UnusupportedType = Cpp.global; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: + let local: Cpp.UnusupportedType = Cpp.global; +} + +// CHECK:STDOUT: --- import_global_scope.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] +// CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .global = %global.var +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %global.var: ref %C = var %global.var_patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyF = %MyF.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "global_scope.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MyF() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %local.patt: %pattern_type = binding_pattern local [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc7_22: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %global.ref: ref %C = name_ref global, imports.%global.var [concrete = imports.%global.var] +// CHECK:STDOUT: %.loc7_17: type = splice_block %C.ref [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc7_14: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc7_25: %C = bind_value %global.ref +// CHECK:STDOUT: %local: %C = bind_name local, %.loc7_25 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_namespace.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] +// CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// 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: .global = %global.var +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %global.var: ref %C = var %global.var_patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyF = %MyF.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "namespace.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MyF() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %local.patt: %pattern_type = binding_pattern local [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc7_24: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc7_27: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %global.ref: ref %C = name_ref global, imports.%global.var [concrete = imports.%global.var] +// CHECK:STDOUT: %.loc7_19: type = splice_block %C.ref [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc7_14: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %N.ref.loc7_17: = name_ref N, imports.%N [concrete = imports.%N] +// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc7_29: %C = bind_value %global.ref +// CHECK:STDOUT: %local: %C = bind_name local, %.loc7_29 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_unsupported_type.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] +// CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .UnusupportedType = +// CHECK:STDOUT: .global = +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyF = %MyF.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "unsupported_type.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MyF() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %local.patt: = binding_pattern local [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc22_37: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %global.ref: = name_ref global, [concrete = ] +// CHECK:STDOUT: %.1: = splice_block [concrete = ] { +// CHECK:STDOUT: %Cpp.ref.loc22_14: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %UnusupportedType.ref: = name_ref UnusupportedType, [concrete = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: %local: = bind_name local, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/lower/mangler.cpp b/toolchain/lower/mangler.cpp index e6d01bf5f8fb..45e7ae8ce79f 100644 --- a/toolchain/lower/mangler.cpp +++ b/toolchain/lower/mangler.cpp @@ -151,7 +151,7 @@ auto Mangler::Mangle(SemIR::FunctionId function_id, return "main"; } if (function.clang_decl_id.has_value()) { - return MangleCppClang(dyn_cast( + return MangleCppClang(cast( sem_ir().clang_decls().Get(function.clang_decl_id).decl)); } RawStringOstream os; @@ -205,10 +205,15 @@ auto Mangler::MangleGlobalVariable(SemIR::InstId pattern_id) -> std::string { return std::string(); } + auto var_name = sem_ir().entity_names().Get(var_name_id); + if (var_name.clang_decl_id.has_value()) { + return MangleCppClang(cast( + sem_ir().clang_decls().Get(var_name.clang_decl_id).decl)); + } + RawStringOstream os; os << "_C"; - auto var_name = sem_ir().entity_names().Get(var_name_id); MangleNameId(os, var_name.name_id); // TODO: If the variable is private, also include the library name as part of // the mangling. diff --git a/toolchain/lower/testdata/interop/cpp/extern_c.carbon b/toolchain/lower/testdata/interop/cpp/extern_c.carbon index 4b32fd96e60f..eeadbdf81e12 100644 --- a/toolchain/lower/testdata/interop/cpp/extern_c.carbon +++ b/toolchain/lower/testdata/interop/cpp/extern_c.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -28,6 +28,44 @@ fn MyF() { Cpp.foo(); } +// ============================================================================ +// extern "C" variable +// ============================================================================ + +// --- extern_c_variable.h + +extern "C" int foo; + +// --- import_extern_c_variable.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "extern_c_variable.h"; + +fn MyF() -> i32 { + return Cpp.foo; +} + +// ============================================================================ +// extern "C" function with C++ special name +// ============================================================================ + +// --- extern_c_with_special_name.h + +struct X {}; + +extern "C" X operator+(X, X); + +// --- 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 { + return a + b; +} + // ============================================================================ // extern "C" function with asm label // ============================================================================ @@ -74,6 +112,85 @@ fn MyF() { // CHECK:STDOUT: !9 = !{} // CHECK:STDOUT: !10 = !DILocation(line: 7, column: 3, scope: !7) // CHECK:STDOUT: !11 = !DILocation(line: 6, column: 1, scope: !7) +// CHECK:STDOUT: ; ModuleID = 'import_extern_c_variable.carbon' +// CHECK:STDOUT: source_filename = "import_extern_c_variable.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: @foo = external global i32 +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CMyF.Main() !dbg !7 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc7 = load i32, ptr @foo, align 4, !dbg !10 +// CHECK:STDOUT: ret i32 %.loc7, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// 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 1, !"wchar_size", i32 4} +// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0} +// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "import_extern_c_variable.carbon", directory: "") +// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "MyF", linkageName: "_CMyF.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !8 = !DISubroutineType(types: !9) +// CHECK:STDOUT: !9 = !{} +// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 10, scope: !7) +// CHECK:STDOUT: !11 = !DILocation(line: 7, column: 3, scope: !7) +// CHECK:STDOUT: ; ModuleID = 'import_extern_c_with_special_name.carbon' +// CHECK:STDOUT: source_filename = "import_extern_c_with_special_name.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: %struct.X = type { i8 } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CMyF.Main(ptr sret({}) %return, ptr %a, ptr %b) !dbg !7 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_Zpl1XS_.carbon_thunk(ptr %a, ptr %b, ptr %return), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_Zpl1XS_(ptr sret({}), ptr, ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress +// CHECK:STDOUT: define dso_local void @_Zpl1XS_.carbon_thunk(ptr %0, ptr %1, ptr %return) #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.addr = alloca ptr, align 8 +// CHECK:STDOUT: %.addr1 = alloca ptr, align 8 +// CHECK:STDOUT: %return.addr = alloca ptr, align 8 +// CHECK:STDOUT: %agg.tmp = alloca %struct.X, align 1 +// CHECK:STDOUT: %agg.tmp2 = alloca %struct.X, align 1 +// CHECK:STDOUT: %undef.agg.tmp = alloca %struct.X, align 1 +// CHECK:STDOUT: store ptr %0, ptr %.addr, align 8 +// CHECK:STDOUT: store ptr %1, ptr %.addr1, align 8 +// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8 +// CHECK:STDOUT: %2 = load ptr, ptr %return.addr, align 8 +// CHECK:STDOUT: %3 = load ptr, ptr %.addr, align 8 +// CHECK:STDOUT: %4 = load ptr, ptr %.addr1, align 8 +// CHECK:STDOUT: call void @_Zpl1XS_() +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// 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 1, !"wchar_size", i32 4} +// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0} +// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "import_extern_c_with_special_name.carbon", directory: "") +// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "MyF", linkageName: "_CMyF.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !8 = !DISubroutineType(types: !9) +// CHECK:STDOUT: !9 = !{} +// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 10, scope: !7) +// CHECK:STDOUT: !11 = !DILocation(line: 7, column: 3, scope: !7) // CHECK:STDOUT: ; ModuleID = 'import_extern_c_with_asm_label.carbon' // CHECK:STDOUT: source_filename = "import_extern_c_with_asm_label.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" diff --git a/toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon b/toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon deleted file mode 100644 index 5f6c3623ddea..000000000000 --- a/toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon +++ /dev/null @@ -1,61 +0,0 @@ -// 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 -// -// AUTOUPDATE -// TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/fail_extern_c.carbon - -// These tests were factored out of `extern_c.carbon` because we do not generate -// LLVM IR if any test contains errors. They should be moved back once they can -// successfully compile. - -// ============================================================================ -// extern "C" variable -// ============================================================================ - -// --- extern_c_variable.h - -extern "C" int foo; - -// --- fail_todo_import_extern_c_variable.carbon - -library "[[@TEST_NAME]]"; - -// CHECK:STDERR: fail_todo_import_extern_c_variable.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./extern_c_variable.h:2:16: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo] -// CHECK:STDERR: extern "C" int foo; -// CHECK:STDERR: ^ -import Cpp library "extern_c_variable.h"; - -fn MyF() -> i32 { - // CHECK:STDERR: fail_todo_import_extern_c_variable.carbon:[[@LINE+4]]:10: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: return Cpp.foo; - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: - return Cpp.foo; -} - -// ============================================================================ -// extern "C" function with C++ special name -// ============================================================================ - -// --- extern_c_with_special_name.h - -struct X {}; - -extern "C" X operator+(X, X); - -// --- 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 { - return a + b; -} diff --git a/toolchain/lower/testdata/interop/cpp/globals.carbon b/toolchain/lower/testdata/interop/cpp/globals.carbon new file mode 100644 index 000000000000..a04ead2a8afc --- /dev/null +++ b/toolchain/lower/testdata/interop/cpp/globals.carbon @@ -0,0 +1,57 @@ +// 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/destroy.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/globals.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/globals.carbon + +// ============================================================================ +// Global +// ============================================================================ + +// --- global.h + +class C {}; +C global; + +// --- import_global.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "global.h"; + +fn MyF() { + let local: Cpp.C = Cpp.global; +} + +// CHECK:STDOUT: ; ModuleID = 'import_global.carbon' +// CHECK:STDOUT: source_filename = "import_global.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: @global = external global {} +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CMyF.Main() !dbg !7 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !10 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// 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 1, !"wchar_size", i32 4} +// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0} +// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "import_global.carbon", directory: "") +// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "MyF", linkageName: "_CMyF.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !8 = !DISubroutineType(types: !9) +// CHECK:STDOUT: !9 = !{} +// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 1, scope: !7) diff --git a/toolchain/sem_ir/entity_name.h b/toolchain/sem_ir/entity_name.h index 63239a80214c..d717cf90fb73 100644 --- a/toolchain/sem_ir/entity_name.h +++ b/toolchain/sem_ir/entity_name.h @@ -8,6 +8,7 @@ #include "common/hashing.h" #include "common/set.h" #include "toolchain/base/value_store.h" +#include "toolchain/sem_ir/clang_decl.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::SemIR { @@ -16,7 +17,7 @@ struct EntityName : public Printable { auto Print(llvm::raw_ostream& out) const -> void { out << "{name: " << name_id << ", parent_scope: " << parent_scope_id << ", index: " << bind_index_value << ", is_template: " << is_template - << "}"; + << ", clang_decl_id: " << clang_decl_id << "}"; } friend auto CarbonHashtableEq(const EntityName& lhs, const EntityName& rhs) @@ -55,6 +56,10 @@ struct EntityName : public Printable { int32_t bind_index_value : 31 = CompileTimeBindIndex::None.index; // Whether this binding is a template parameter. bool is_template : 1 = false; + // For imported C++ global variable names, the Clang decl to use for mangling. + // TODO: Move the mapping between global variables and the clang decl to avoid + // paying extra memory when the names are not imported from C++. + ClangDeclId clang_decl_id = ClangDeclId::None; }; // Value store for EntityName. In addition to the regular ValueStore @@ -63,12 +68,14 @@ struct EntityNameStore : public ValueStore { public: // Adds an entity name for a symbolic binding. auto AddSymbolicBindingName(NameId name_id, NameScopeId parent_scope_id, - CompileTimeBindIndex bind_index, bool is_template) + CompileTimeBindIndex bind_index, bool is_template, + ClangDeclId clang_decl_id = ClangDeclId::None) -> EntityNameId { return Add({.name_id = name_id, .parent_scope_id = parent_scope_id, .bind_index_value = bind_index.index, - .is_template = is_template}); + .is_template = is_template, + .clang_decl_id = clang_decl_id}); } // Convert an `EntityName` to a canonical ID. All calls to this with