diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 39837f151992..a92ae4af5469 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -89,7 +89,7 @@ static auto TrackImport(Map& api_map, unit_info.emitter.Emit(import.node_id, CppInteropFuzzing); return; } - unit_info.cpp_imports.push_back(import); + unit_info.cpp_import_names.push_back(import); return; } diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index ac9bb4b0f4dc..db0d4e2786f1 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -131,7 +131,6 @@ auto CheckUnit::InitPackageScopeAndImports() -> void { package_imports.node_id, {.package_id = SemIR::NameId::ForPackageName( package_imports.package_id)}); } - // Process the imports. if (unit_and_imports_->api_for_impl) { ImportApiFile(context_, namespace_type_id, @@ -140,7 +139,8 @@ auto CheckUnit::InitPackageScopeAndImports() -> void { ImportCurrentPackage(package_inst_id, namespace_type_id); CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package); ImportOtherPackages(namespace_type_id); - ImportCppPackages(); + ImportCppFiles(context_, unit_and_imports_->unit->sem_ir->filename(), + unit_and_imports_->cpp_import_names, fs_); } auto CheckUnit::CollectDirectImports( @@ -342,25 +342,6 @@ auto CheckUnit::ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void { } } -auto CheckUnit::ImportCppPackages() -> void { - const auto& imports = unit_and_imports_->cpp_imports; - if (imports.empty()) { - return; - } - - llvm::SmallVector> import_pairs; - import_pairs.reserve(imports.size()); - for (const auto& import : imports) { - import_pairs.push_back( - {unit_and_imports_->unit->value_stores->string_literal_values().Get( - import.library_id), - import.node_id}); - } - - ImportCppFiles(context_, unit_and_imports_->unit->sem_ir->filename(), - import_pairs, fs_); -} - // Loops over all nodes in the tree. On some errors, this may return early, // for example if an unrecoverable state is encountered. // NOLINTNEXTLINE(readability-function-size) diff --git a/toolchain/check/check_unit.h b/toolchain/check/check_unit.h index 05128c6a6c72..5562643b115e 100644 --- a/toolchain/check/check_unit.h +++ b/toolchain/check/check_unit.h @@ -94,7 +94,7 @@ struct UnitAndImports { Map package_imports_map; // List of the `import Cpp` imports. - llvm::SmallVector cpp_imports; + llvm::SmallVector cpp_import_names; // The remaining number of imports which must be checked before this unit can // be processed. @@ -153,9 +153,6 @@ class CheckUnit { // Imports all other Carbon packages (excluding the current package). auto ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void; - // Imports all C++ packages. - auto ImportCppPackages() -> void; - // Checks that each required definition is available. If the definition can be // generated by resolving a specific, does so, otherwise emits a diagnostic // for each declaration in context.definitions_required() that doesn't have a diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index bea162b24a25..78d259a4a7f3 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -2119,6 +2119,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context, case SemIR::Branch::Kind: case SemIR::BranchIf::Kind: case SemIR::BranchWithArg::Kind: + case SemIR::ImportCppDecl::Kind: case SemIR::ImportDecl::Kind: case SemIR::NameBindingDecl::Kind: case SemIR::OutParam::Kind: diff --git a/toolchain/check/import.cpp b/toolchain/check/import.cpp index e301531c45e0..bd865d0d09d1 100644 --- a/toolchain/check/import.cpp +++ b/toolchain/check/import.cpp @@ -85,23 +85,12 @@ static auto CopyNameFromImportIR(Context& context, return import_name_id; } -namespace { -struct NamespaceResult { - SemIR::NameScopeId name_scope_id; - SemIR::InstId inst_id; - bool is_duplicate_of_namespace_in_current_package; -}; -} // namespace - -// Adds a namespace to the IR. The bool on return is true if there was a name -// conflict. diagnose_duplicate_namespace is used when handling a cross-package -// import, where an existing namespace is in the current package and the new -// namespace is a different package. -static auto AddNamespace( - Context& context, SemIR::TypeId namespace_type_id, SemIR::NameId name_id, - SemIR::NameScopeId parent_scope_id, bool diagnose_duplicate_namespace, - llvm::function_refSemIR::InstId> make_import_id) - -> NamespaceResult { +auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id, + SemIR::NameId name_id, + SemIR::NameScopeId parent_scope_id, + bool diagnose_duplicate_namespace, + llvm::function_ref make_import_id) + -> AddImportNamespaceResult { auto* parent_scope = &context.name_scopes().Get(parent_scope_id); auto [inserted, entry_id] = parent_scope->LookupOrAdd( name_id, @@ -186,7 +175,7 @@ static auto CopySingleNameScopeFromImportIR( Map* copied_namespaces, SemIR::ImportIRId ir_id, SemIR::InstId import_inst_id, SemIR::NameScopeId import_scope_id, SemIR::NameScopeId parent_scope_id, - SemIR::NameId name_id) -> NamespaceResult { + SemIR::NameId name_id) -> AddImportNamespaceResult { // Produce the namespace for the entry. auto make_import_id = [&]() { auto entity_name_id = context.entity_names().Add( @@ -203,9 +192,9 @@ static auto CopySingleNameScopeFromImportIR( context.import_ref_ids().push_back(inst_id); return inst_id; }; - NamespaceResult result = - AddNamespace(context, namespace_type_id, name_id, parent_scope_id, - /*diagnose_duplicate_namespace=*/false, make_import_id); + AddImportNamespaceResult result = AddImportNamespace( + context, namespace_type_id, name_id, parent_scope_id, + /*diagnose_duplicate_namespace=*/false, make_import_id); auto namespace_const_id = context.constant_values().Get(result.inst_id); context.import_ir_constant_values()[ir_id.index].Set(import_inst_id, @@ -458,7 +447,7 @@ auto ImportLibrariesFromOtherPackage(Context& context, auto name_id = SemIR::NameId::ForPackageName(package_id); - NamespaceResult result = AddNamespace( + AddImportNamespaceResult result = AddImportNamespace( context, namespace_type_id, name_id, SemIR::NameScopeId::Package, /*diagnose_duplicate_namespace=*/true, [&] { return import_decl_id; }); auto namespace_const_id = context.constant_values().Get(result.inst_id); @@ -525,7 +514,7 @@ static auto AddNamespaceFromOtherPackage(Context& context, -> SemIR::InstId { auto namespace_type_id = context.GetSingletonType(SemIR::NamespaceType::SingletonInstId); - NamespaceResult result = CopySingleNameScopeFromImportIR( + AddImportNamespaceResult result = CopySingleNameScopeFromImportIR( context, namespace_type_id, /*copied_namespaces=*/nullptr, import_ir_id, import_inst_id, import_ns.name_scope_id, parent_scope_id, name_id); auto& scope = context.name_scopes().Get(result.name_scope_id); diff --git a/toolchain/check/import.h b/toolchain/check/import.h index 28cadb76f202..443d77b7e8b8 100644 --- a/toolchain/check/import.h +++ b/toolchain/check/import.h @@ -11,6 +11,23 @@ namespace Carbon::Check { +struct AddImportNamespaceResult { + SemIR::NameScopeId name_scope_id; + SemIR::InstId inst_id; + bool is_duplicate_of_namespace_in_current_package; +}; + +// Adds a namespace to the IR. The bool on return is true if there was a name +// conflict. diagnose_duplicate_namespace is used when handling a cross-package +// import, where an existing namespace is in the current package and the new +// namespace is a different package. +auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id, + SemIR::NameId name_id, + SemIR::NameScopeId parent_scope_id, + bool diagnose_duplicate_namespace, + llvm::function_ref make_import_id) + -> AddImportNamespaceResult; + // Imports the API file's name lookup information into a corresponding // implementation file. Only information for the current package will be copied; // information for other packages should be handled through diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index c4861a68657b..286e9510a2e4 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -5,6 +5,7 @@ #include "toolchain/check/import_cpp.h" #include +#include #include #include "clang/Frontend/TextDiagnosticPrinter.h" @@ -15,35 +16,39 @@ #include "llvm/Support/raw_ostream.h" #include "toolchain/check/context.h" #include "toolchain/check/diagnostic_helpers.h" +#include "toolchain/check/import.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/diagnostics/format_providers.h" +#include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { // Generates C++ file contents to #include all requested imports. static auto GenerateCppIncludesHeaderCode( - llvm::ArrayRef> imports) + Context& context, llvm::ArrayRef imports) -> std::string { std::string code; llvm::raw_string_ostream code_stream(code); - for (const auto& [path, _] : imports) { - code_stream << "#include \"" << FormatEscaped(path) << "\"\n"; + for (const Parse::Tree::PackagingNames& import : imports) { + code_stream << "#include \"" + << FormatEscaped( + context.string_literal_values().Get(import.library_id)) + << "\"\n"; } return code; } -auto ImportCppFiles( - Context& context, llvm::StringRef importing_file_path, - llvm::ArrayRef> imports, - llvm::IntrusiveRefCntPtr fs) -> void { - size_t num_imports = imports.size(); - if (num_imports == 0) { - return; - } - +// Returns an AST for the C++ imports and a bool that represents whether +// compilation errors where encountered or the generated AST is null due to an +// error. +// TODO: Consider to always have a (non-null) AST. +static auto GenerateAst(Context& context, llvm::StringRef importing_file_path, + llvm::ArrayRef imports, + llvm::IntrusiveRefCntPtr fs) + -> std::pair, bool> { // TODO: Use all import locations by referring each Clang diagnostic to the // relevant import. - SemIRLoc loc = imports.back().second; + SemIRLoc loc = imports.back().node_id; std::string diagnostics_str; llvm::raw_string_ostream diagnostics_stream(diagnostics_str); @@ -54,7 +59,7 @@ auto ImportCppFiles( diagnostic_options.get()); // TODO: Share compilation flags with ClangRunner. auto ast = clang::tooling::buildASTFromCodeWithArgs( - GenerateCppIncludesHeaderCode(imports), {}, + GenerateCppIncludesHeaderCode(context, imports), {}, (importing_file_path + ".generated.cpp_imports.h").str(), "clang-tool", std::make_shared(), clang::tooling::getClangStripDependencyFileAdjuster(), @@ -62,6 +67,7 @@ auto ImportCppFiles( // TODO: Implement and use a DynamicRecursiveASTVisitor to traverse the AST. int num_errors = diagnostics_consumer.getNumErrors(); int num_warnings = diagnostics_consumer.getNumWarnings(); + int num_imports = imports.size(); if (num_errors > 0) { // TODO: Remove the warnings part when there are no warnings. CARBON_DIAGNOSTIC( @@ -77,6 +83,56 @@ auto ImportCppFiles( context.emitter().Emit(loc, CppInteropParseWarning, num_warnings, num_imports, diagnostics_str); } + return {std::move(ast), !ast || num_errors > 0}; +} + +// Adds a namespace for the `Cpp` import and returns its `NameScopeId`. +static auto AddNamespace(Context& context, PackageNameId cpp_package_id, + llvm::ArrayRef imports) + -> SemIR::NameScopeId { + auto& import_cpps = context.sem_ir().import_cpps(); + import_cpps.Reserve(imports.size()); + for (const Parse::Tree::PackagingNames& import : imports) { + import_cpps.Add( + {.node_id = import.node_id, .library_id = import.library_id}); + } + + return AddImportNamespace( + context, + context.GetSingletonType(SemIR::NamespaceType::SingletonInstId), + SemIR::NameId::ForPackageName(cpp_package_id), + SemIR::NameScopeId::Package, + /*diagnose_duplicate_namespace=*/false, + [&]() { + return context.AddInst( + imports.front().node_id, {}); + }) + .name_scope_id; +} + +auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path, + llvm::ArrayRef imports, + llvm::IntrusiveRefCntPtr fs) + -> void { + if (imports.empty()) { + return; + } + + auto [ast, ast_has_error] = + GenerateAst(context, importing_file_path, imports, fs); + + PackageNameId package_id = imports.front().package_id; + CARBON_CHECK( + llvm::all_of(imports, [&](const Parse::Tree::PackagingNames& import) { + return import.package_id == package_id; + })); + auto name_scope_id = AddNamespace(context, package_id, imports); + SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id); + name_scope.set_is_closed_import(true); + + if (ast_has_error) { + name_scope.set_has_error(); + } } } // namespace Carbon::Check diff --git a/toolchain/check/import_cpp.h b/toolchain/check/import_cpp.h index db638e964b80..886a983f0a5e 100644 --- a/toolchain/check/import_cpp.h +++ b/toolchain/check/import_cpp.h @@ -12,11 +12,10 @@ namespace Carbon::Check { // Generates a C++ header that includes the imported cpp files, parses it and -// report errors and warnings. -auto ImportCppFiles( - Context& context, llvm::StringRef importing_file_path, - llvm::ArrayRef> imports, - llvm::IntrusiveRefCntPtr fs) -> void; +// report errors and warnings. If successful, adds a `Cpp` namespace. +auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path, + llvm::ArrayRef imports, + llvm::IntrusiveRefCntPtr fs) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon index 442a5d3816c0..c03da5ce11b1 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon @@ -56,7 +56,18 @@ import Cpp library "\"foo.h\""; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_cpp_library_file_with_quotes.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "\"foo.h\"" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon index 27173cdf6caf..f4d87bf326df 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon @@ -217,49 +217,133 @@ import Cpp library "multiple_errors_and_multiple_warnings.h"; // CHECK:STDOUT: --- fail_import_cpp_file_with_one_error.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "one_error.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_cpp_file_with_multiple_errors.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "multiple_errors.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_cpp_file_with_one_warning.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "one_warning.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_cpp_file_with_multiple_warnings.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "multiple_warnings.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_cpp_file_with_one_error_and_one_warning.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "one_error_and_one_warning.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_cpp_file_with_multiple_errors_and_multiple_warnings.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "multiple_errors_and_multiple_warnings.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_multiple_cpp_files_with_warnings.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "one_warning.h" +// CHECK:STDOUT: import Cpp "multiple_warnings.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_multiple_cpp_files_with_warnings_and_errors.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "one_error_and_one_warning.h" +// CHECK:STDOUT: import Cpp "multiple_errors_and_multiple_warnings.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/cpp_namespace.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/cpp_namespace.carbon new file mode 100644 index 000000000000..b84a5c29a20f --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/no_prelude/cpp_namespace.carbon @@ -0,0 +1,187 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/cpp_namespace.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/cpp_namespace.carbon + +// --- header.h + +// --- fail_duplicate_cpp_name.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "header.h"; + +// CHECK:STDERR: fail_duplicate_cpp_name.carbon:[[@LINE+7]]:1: error: duplicate name being declared in the same scope [NameDeclDuplicate] +// CHECK:STDERR: namespace Cpp; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: fail_duplicate_cpp_name.carbon:[[@LINE-5]]:1: note: name is previously declared here [NameDeclPrevious] +// CHECK:STDERR: import Cpp library "header.h"; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +namespace Cpp; + +// --- cpp_in_inner_namespace.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "header.h"; + +namespace N; +namespace N.Cpp; + +// --- alias.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "header.h"; + +alias MyCpp = Cpp; + +// --- fail_add_name_to_cpp_namespace.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "header.h"; + +// CHECK:STDERR: fail_add_name_to_cpp_namespace.carbon:[[@LINE+7]]:7: error: imported packages cannot be used for declarations [QualifiedDeclOutsidePackage] +// CHECK:STDERR: class Cpp.C {}; +// CHECK:STDERR: ^~~ +// CHECK:STDERR: fail_add_name_to_cpp_namespace.carbon:[[@LINE-5]]:1: note: package imported here [QualifiedDeclOutsidePackageSource] +// CHECK:STDERR: import Cpp library "header.h"; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +class Cpp.C {}; + +// --- api_and_impl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "header.h"; + +// --- api_and_impl.impl.carbon + +impl library "[[@TEST_NAME]]"; + +import Cpp library "header.h"; + +// CHECK:STDOUT: --- fail_duplicate_cpp_name.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "header.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- cpp_in_inner_namespace.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "header.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %N: = namespace [template] { +// CHECK:STDOUT: .Cpp = %Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyCpp = %MyCpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "header.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [template = imports.%Cpp] +// CHECK:STDOUT: %MyCpp: = bind_alias MyCpp, imports.%Cpp [template = imports.%Cpp] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_add_name_to_cpp_namespace.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: .C = file.%C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "header.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- api_and_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "header.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- api_and_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Cpp: = import_ref Main//api_and_impl, Cpp, loaded +// CHECK:STDOUT: %Cpp: = namespace %Main.Cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import.loc2_6.1 = import +// CHECK:STDOUT: %default.import.loc2_6.2 = import +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon index 9e6e8f8061fd..6bb4a5ad66ba 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon @@ -24,7 +24,18 @@ import Cpp library "not_found.h"; // CHECK:STDOUT: --- fail_cpp_file_not_found.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] { +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "not_found.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon index 79b06f2ced7b..4f1405e52e81 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon @@ -20,7 +20,16 @@ import Cpp library "function_decl.h"; // CHECK:STDOUT: --- import_function_decl.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "function_decl.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/include.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/include.carbon index 58bfb3d68121..d80aaab5eef0 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/include.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/include.carbon @@ -24,7 +24,16 @@ import Cpp library "including_file.h"; // CHECK:STDOUT: --- import_function_decl.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "including_file.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon index 4ae55ebb3c98..5c1ac5f0ec01 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon @@ -25,7 +25,17 @@ import Cpp library "file2.h"; // CHECK:STDOUT: --- multiple_imports.carbon // CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "file1.h" +// CHECK:STDOUT: import Cpp "file2.h" +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 0f7088a1398b..7043592ac9f8 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -93,6 +93,7 @@ cc_library( "function.h", "generic.h", "impl.h", + "import_cpp.h", "import_ir.h", "interface.h", "name.h", diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index ff3d5092b733..fb095b82fc71 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -268,6 +268,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case GenericInterfaceType::Kind: case ImplWitness::Kind: case ImplWitnessAccess::Kind: + case ImportCppDecl::Kind: case ImportDecl::Kind: case IntLiteralType::Kind: case IntType::Kind: diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 083b60207d11..dc11be538b66 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -24,6 +24,7 @@ #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/impl.h" +#include "toolchain/sem_ir/import_cpp.h" #include "toolchain/sem_ir/import_ir.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/interface.h" @@ -166,6 +167,10 @@ class File : public Printable { auto import_ir_insts() const -> const ValueStore& { return import_ir_insts_; } + auto import_cpps() -> ValueStore& { return import_cpps_; } + auto import_cpps() const -> const ValueStore& { + return import_cpps_; + } auto names() const -> NameStoreWrapper { return NameStoreWrapper(&identifiers()); } @@ -275,6 +280,9 @@ class File : public Printable { // that are import-related. ValueStore import_ir_insts_; + // List of Cpp imports. + ValueStore import_cpps_; + // Type blocks within the IR. These reference entries in types_. Storage for // the data is provided by allocator_. BlockValueStore type_blocks_; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 4d0ab3e55a03..6b4b78b4b648 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -846,6 +846,12 @@ class FormatterImpl { } } + // Format ImportCppDecl name. + auto FormatInstLHS(InstId inst_id, ImportCppDecl /*inst*/) -> void { + FormatName(inst_id); + out_ << " = "; + } + // Format ImportDecl with its name. auto FormatInstLHS(InstId inst_id, ImportDecl /*inst*/) -> void { FormatName(inst_id); @@ -1078,6 +1084,20 @@ class FormatterImpl { out_ << " " << buffer; } + // Format the metadata in File for `import Cpp`. + auto FormatInstRHS(ImportCppDecl /*inst*/) -> void { + out_ << " "; + OpenBrace(); + for (ImportCpp import_cpp : sem_ir_->import_cpps().array_ref()) { + Indent(); + out_ << "import Cpp \"" + << FormatEscaped( + sem_ir_->string_literal_values().Get(import_cpp.library_id)) + << "\"\n"; + } + CloseBrace(); + } + auto FormatImportRefRHS(ImportIRInstId import_ir_inst_id, EntityNameId entity_name_id, llvm::StringLiteral loaded_label) -> void { diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 4d93690d56ba..77ae32830140 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -26,6 +26,7 @@ struct FacetTypeInfo; struct Function; struct Generic; struct Specific; +struct ImportCpp; struct ImportIR; struct ImportIRInst; struct Impl; @@ -377,6 +378,18 @@ struct GenericInstIndex : public IndexBase { constexpr GenericInstIndex GenericInstIndex::None = GenericInstIndex::MakeNone(); +struct ImportCppId : public IdBase { + static constexpr llvm::StringLiteral Label = "import_cpp"; + using ValueType = ImportCpp; + + // An ID with no value. + static const ImportCppId None; + + using IdBase::IdBase; +}; + +constexpr ImportCppId ImportCppId::None = ImportCppId(NoneIndex); + // The ID of an IR within the set of imported IRs, both direct and indirect. struct ImportIRId : public IdBase { static constexpr llvm::StringLiteral Label = "ir"; diff --git a/toolchain/sem_ir/import_cpp.h b/toolchain/sem_ir/import_cpp.h new file mode 100644 index 000000000000..eb72a2af87f6 --- /dev/null +++ b/toolchain/sem_ir/import_cpp.h @@ -0,0 +1,25 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_SEM_IR_IMPORT_CPP_H_ +#define CARBON_TOOLCHAIN_SEM_IR_IMPORT_CPP_H_ + +#include "common/raw_string_ostream.h" +#include "toolchain/sem_ir/typed_insts.h" + +namespace Carbon::SemIR { + +// Per `import Cpp` data. +struct ImportCpp : Printable { + auto Print(llvm::raw_ostream& out) const -> void { + out << "{node_id: " << node_id << ", library_id: " << library_id << "}"; + } + + Parse::ImportDeclId node_id; + StringLiteralValueId library_id; +}; + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_IMPORT_CPP_H_ diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 98076d2af428..5798d8db1ea9 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -69,6 +69,7 @@ CARBON_SEM_IR_INST_KIND(GenericInterfaceType) CARBON_SEM_IR_INST_KIND(ImplDecl) CARBON_SEM_IR_INST_KIND(ImplWitness) CARBON_SEM_IR_INST_KIND(ImplWitnessAccess) +CARBON_SEM_IR_INST_KIND(ImportCppDecl) CARBON_SEM_IR_INST_KIND(ImportDecl) CARBON_SEM_IR_INST_KIND(ImportRefLoaded) CARBON_SEM_IR_INST_KIND(ImportRefUnloaded) diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index 4f8250c2bb5f..4a3258842ac7 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -705,6 +705,10 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id, add_inst_name(out.TakeStr()); continue; } + case ImportCppDecl::Kind: { + add_inst_name("Cpp.import_cpp"); + continue; + } case CARBON_KIND(ImportDecl inst): { if (inst.package_id.has_value()) { add_inst_name_id(inst.package_id, ".import"); diff --git a/toolchain/sem_ir/stringify_type.cpp b/toolchain/sem_ir/stringify_type.cpp index fbb38d167c90..97d35776e81c 100644 --- a/toolchain/sem_ir/stringify_type.cpp +++ b/toolchain/sem_ir/stringify_type.cpp @@ -591,6 +591,7 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) case FunctionDecl::Kind: case ImplDecl::Kind: case ImplWitness::Kind: + case ImportCppDecl::Kind: case ImportDecl::Kind: case ImportRefLoaded::Kind: case InitializeFrom::Kind: diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 0502adc85aaf..9f850bc467ba 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -840,6 +840,13 @@ struct ImplWitnessAccess { ElementIndex index; }; +// An `import Cpp` declaration. +struct ImportCppDecl { + static constexpr auto Kind = + InstKind::ImportCppDecl.Define( + {.ir_name = "import_cpp", .is_lowered = false}); +}; + // An `import` declaration. This is mainly for `import` diagnostics, and a 1:1 // correspondence with actual `import`s isn't guaranteed. struct ImportDecl {