Skip to content

Commit 3f599c2

Browse files
authored
Generate Cpp namespace when import Cpp is used (#4873)
Also defined a dedicated `ImportCppDecl` `InstKind`. Part of #4666
1 parent 6c4767e commit 3f599c2

25 files changed

+523
-81
lines changed

toolchain/check/check.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ static auto TrackImport(Map<ImportKey, UnitAndImports*>& api_map,
8989
unit_info.emitter.Emit(import.node_id, CppInteropFuzzing);
9090
return;
9191
}
92-
unit_info.cpp_imports.push_back(import);
92+
unit_info.cpp_import_names.push_back(import);
9393
return;
9494
}
9595

toolchain/check/check_unit.cpp

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ auto CheckUnit::InitPackageScopeAndImports() -> void {
131131
package_imports.node_id, {.package_id = SemIR::NameId::ForPackageName(
132132
package_imports.package_id)});
133133
}
134-
135134
// Process the imports.
136135
if (unit_and_imports_->api_for_impl) {
137136
ImportApiFile(context_, namespace_type_id,
@@ -140,7 +139,8 @@ auto CheckUnit::InitPackageScopeAndImports() -> void {
140139
ImportCurrentPackage(package_inst_id, namespace_type_id);
141140
CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package);
142141
ImportOtherPackages(namespace_type_id);
143-
ImportCppPackages();
142+
ImportCppFiles(context_, unit_and_imports_->unit->sem_ir->filename(),
143+
unit_and_imports_->cpp_import_names, fs_);
144144
}
145145

146146
auto CheckUnit::CollectDirectImports(
@@ -342,25 +342,6 @@ auto CheckUnit::ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void {
342342
}
343343
}
344344

345-
auto CheckUnit::ImportCppPackages() -> void {
346-
const auto& imports = unit_and_imports_->cpp_imports;
347-
if (imports.empty()) {
348-
return;
349-
}
350-
351-
llvm::SmallVector<std::pair<llvm::StringRef, SemIRLoc>> import_pairs;
352-
import_pairs.reserve(imports.size());
353-
for (const auto& import : imports) {
354-
import_pairs.push_back(
355-
{unit_and_imports_->unit->value_stores->string_literal_values().Get(
356-
import.library_id),
357-
import.node_id});
358-
}
359-
360-
ImportCppFiles(context_, unit_and_imports_->unit->sem_ir->filename(),
361-
import_pairs, fs_);
362-
}
363-
364345
// Loops over all nodes in the tree. On some errors, this may return early,
365346
// for example if an unrecoverable state is encountered.
366347
// NOLINTNEXTLINE(readability-function-size)

toolchain/check/check_unit.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ struct UnitAndImports {
9494
Map<PackageNameId, int32_t> package_imports_map;
9595

9696
// List of the `import Cpp` imports.
97-
llvm::SmallVector<Parse::Tree::PackagingNames> cpp_imports;
97+
llvm::SmallVector<Parse::Tree::PackagingNames> cpp_import_names;
9898

9999
// The remaining number of imports which must be checked before this unit can
100100
// be processed.
@@ -153,9 +153,6 @@ class CheckUnit {
153153
// Imports all other Carbon packages (excluding the current package).
154154
auto ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void;
155155

156-
// Imports all C++ packages.
157-
auto ImportCppPackages() -> void;
158-
159156
// Checks that each required definition is available. If the definition can be
160157
// generated by resolving a specific, does so, otherwise emits a diagnostic
161158
// for each declaration in context.definitions_required() that doesn't have a

toolchain/check/eval.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
21192119
case SemIR::Branch::Kind:
21202120
case SemIR::BranchIf::Kind:
21212121
case SemIR::BranchWithArg::Kind:
2122+
case SemIR::ImportCppDecl::Kind:
21222123
case SemIR::ImportDecl::Kind:
21232124
case SemIR::NameBindingDecl::Kind:
21242125
case SemIR::OutParam::Kind:

toolchain/check/import.cpp

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,23 +85,12 @@ static auto CopyNameFromImportIR(Context& context,
8585
return import_name_id;
8686
}
8787

88-
namespace {
89-
struct NamespaceResult {
90-
SemIR::NameScopeId name_scope_id;
91-
SemIR::InstId inst_id;
92-
bool is_duplicate_of_namespace_in_current_package;
93-
};
94-
} // namespace
95-
96-
// Adds a namespace to the IR. The bool on return is true if there was a name
97-
// conflict. diagnose_duplicate_namespace is used when handling a cross-package
98-
// import, where an existing namespace is in the current package and the new
99-
// namespace is a different package.
100-
static auto AddNamespace(
101-
Context& context, SemIR::TypeId namespace_type_id, SemIR::NameId name_id,
102-
SemIR::NameScopeId parent_scope_id, bool diagnose_duplicate_namespace,
103-
llvm::function_ref<auto()->SemIR::InstId> make_import_id)
104-
-> NamespaceResult {
88+
auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id,
89+
SemIR::NameId name_id,
90+
SemIR::NameScopeId parent_scope_id,
91+
bool diagnose_duplicate_namespace,
92+
llvm::function_ref<SemIR::InstId()> make_import_id)
93+
-> AddImportNamespaceResult {
10594
auto* parent_scope = &context.name_scopes().Get(parent_scope_id);
10695
auto [inserted, entry_id] = parent_scope->LookupOrAdd(
10796
name_id,
@@ -186,7 +175,7 @@ static auto CopySingleNameScopeFromImportIR(
186175
Map<SemIR::NameScopeId, SemIR::NameScopeId>* copied_namespaces,
187176
SemIR::ImportIRId ir_id, SemIR::InstId import_inst_id,
188177
SemIR::NameScopeId import_scope_id, SemIR::NameScopeId parent_scope_id,
189-
SemIR::NameId name_id) -> NamespaceResult {
178+
SemIR::NameId name_id) -> AddImportNamespaceResult {
190179
// Produce the namespace for the entry.
191180
auto make_import_id = [&]() {
192181
auto entity_name_id = context.entity_names().Add(
@@ -203,9 +192,9 @@ static auto CopySingleNameScopeFromImportIR(
203192
context.import_ref_ids().push_back(inst_id);
204193
return inst_id;
205194
};
206-
NamespaceResult result =
207-
AddNamespace(context, namespace_type_id, name_id, parent_scope_id,
208-
/*diagnose_duplicate_namespace=*/false, make_import_id);
195+
AddImportNamespaceResult result = AddImportNamespace(
196+
context, namespace_type_id, name_id, parent_scope_id,
197+
/*diagnose_duplicate_namespace=*/false, make_import_id);
209198

210199
auto namespace_const_id = context.constant_values().Get(result.inst_id);
211200
context.import_ir_constant_values()[ir_id.index].Set(import_inst_id,
@@ -458,7 +447,7 @@ auto ImportLibrariesFromOtherPackage(Context& context,
458447

459448
auto name_id = SemIR::NameId::ForPackageName(package_id);
460449

461-
NamespaceResult result = AddNamespace(
450+
AddImportNamespaceResult result = AddImportNamespace(
462451
context, namespace_type_id, name_id, SemIR::NameScopeId::Package,
463452
/*diagnose_duplicate_namespace=*/true, [&] { return import_decl_id; });
464453
auto namespace_const_id = context.constant_values().Get(result.inst_id);
@@ -525,7 +514,7 @@ static auto AddNamespaceFromOtherPackage(Context& context,
525514
-> SemIR::InstId {
526515
auto namespace_type_id =
527516
context.GetSingletonType(SemIR::NamespaceType::SingletonInstId);
528-
NamespaceResult result = CopySingleNameScopeFromImportIR(
517+
AddImportNamespaceResult result = CopySingleNameScopeFromImportIR(
529518
context, namespace_type_id, /*copied_namespaces=*/nullptr, import_ir_id,
530519
import_inst_id, import_ns.name_scope_id, parent_scope_id, name_id);
531520
auto& scope = context.name_scopes().Get(result.name_scope_id);

toolchain/check/import.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,23 @@
1111

1212
namespace Carbon::Check {
1313

14+
struct AddImportNamespaceResult {
15+
SemIR::NameScopeId name_scope_id;
16+
SemIR::InstId inst_id;
17+
bool is_duplicate_of_namespace_in_current_package;
18+
};
19+
20+
// Adds a namespace to the IR. The bool on return is true if there was a name
21+
// conflict. diagnose_duplicate_namespace is used when handling a cross-package
22+
// import, where an existing namespace is in the current package and the new
23+
// namespace is a different package.
24+
auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id,
25+
SemIR::NameId name_id,
26+
SemIR::NameScopeId parent_scope_id,
27+
bool diagnose_duplicate_namespace,
28+
llvm::function_ref<SemIR::InstId()> make_import_id)
29+
-> AddImportNamespaceResult;
30+
1431
// Imports the API file's name lookup information into a corresponding
1532
// implementation file. Only information for the current package will be copied;
1633
// information for other packages should be handled through

toolchain/check/import_cpp.cpp

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "toolchain/check/import_cpp.h"
66

77
#include <memory>
8+
#include <optional>
89
#include <string>
910

1011
#include "clang/Frontend/TextDiagnosticPrinter.h"
@@ -15,35 +16,39 @@
1516
#include "llvm/Support/raw_ostream.h"
1617
#include "toolchain/check/context.h"
1718
#include "toolchain/check/diagnostic_helpers.h"
19+
#include "toolchain/check/import.h"
1820
#include "toolchain/diagnostics/diagnostic.h"
1921
#include "toolchain/diagnostics/format_providers.h"
22+
#include "toolchain/sem_ir/name_scope.h"
2023

2124
namespace Carbon::Check {
2225

2326
// Generates C++ file contents to #include all requested imports.
2427
static auto GenerateCppIncludesHeaderCode(
25-
llvm::ArrayRef<std::pair<llvm::StringRef, SemIRLoc>> imports)
28+
Context& context, llvm::ArrayRef<Parse::Tree::PackagingNames> imports)
2629
-> std::string {
2730
std::string code;
2831
llvm::raw_string_ostream code_stream(code);
29-
for (const auto& [path, _] : imports) {
30-
code_stream << "#include \"" << FormatEscaped(path) << "\"\n";
32+
for (const Parse::Tree::PackagingNames& import : imports) {
33+
code_stream << "#include \""
34+
<< FormatEscaped(
35+
context.string_literal_values().Get(import.library_id))
36+
<< "\"\n";
3137
}
3238
return code;
3339
}
3440

35-
auto ImportCppFiles(
36-
Context& context, llvm::StringRef importing_file_path,
37-
llvm::ArrayRef<std::pair<llvm::StringRef, SemIRLoc>> imports,
38-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) -> void {
39-
size_t num_imports = imports.size();
40-
if (num_imports == 0) {
41-
return;
42-
}
43-
41+
// Returns an AST for the C++ imports and a bool that represents whether
42+
// compilation errors where encountered or the generated AST is null due to an
43+
// error.
44+
// TODO: Consider to always have a (non-null) AST.
45+
static auto GenerateAst(Context& context, llvm::StringRef importing_file_path,
46+
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
47+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs)
48+
-> std::pair<std::unique_ptr<clang::ASTUnit>, bool> {
4449
// TODO: Use all import locations by referring each Clang diagnostic to the
4550
// relevant import.
46-
SemIRLoc loc = imports.back().second;
51+
SemIRLoc loc = imports.back().node_id;
4752

4853
std::string diagnostics_str;
4954
llvm::raw_string_ostream diagnostics_stream(diagnostics_str);
@@ -54,14 +59,15 @@ auto ImportCppFiles(
5459
diagnostic_options.get());
5560
// TODO: Share compilation flags with ClangRunner.
5661
auto ast = clang::tooling::buildASTFromCodeWithArgs(
57-
GenerateCppIncludesHeaderCode(imports), {},
62+
GenerateCppIncludesHeaderCode(context, imports), {},
5863
(importing_file_path + ".generated.cpp_imports.h").str(), "clang-tool",
5964
std::make_shared<clang::PCHContainerOperations>(),
6065
clang::tooling::getClangStripDependencyFileAdjuster(),
6166
clang::tooling::FileContentMappings(), &diagnostics_consumer, fs);
6267
// TODO: Implement and use a DynamicRecursiveASTVisitor to traverse the AST.
6368
int num_errors = diagnostics_consumer.getNumErrors();
6469
int num_warnings = diagnostics_consumer.getNumWarnings();
70+
int num_imports = imports.size();
6571
if (num_errors > 0) {
6672
// TODO: Remove the warnings part when there are no warnings.
6773
CARBON_DIAGNOSTIC(
@@ -77,6 +83,56 @@ auto ImportCppFiles(
7783
context.emitter().Emit(loc, CppInteropParseWarning, num_warnings,
7884
num_imports, diagnostics_str);
7985
}
86+
return {std::move(ast), !ast || num_errors > 0};
87+
}
88+
89+
// Adds a namespace for the `Cpp` import and returns its `NameScopeId`.
90+
static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
91+
llvm::ArrayRef<Parse::Tree::PackagingNames> imports)
92+
-> SemIR::NameScopeId {
93+
auto& import_cpps = context.sem_ir().import_cpps();
94+
import_cpps.Reserve(imports.size());
95+
for (const Parse::Tree::PackagingNames& import : imports) {
96+
import_cpps.Add(
97+
{.node_id = import.node_id, .library_id = import.library_id});
98+
}
99+
100+
return AddImportNamespace(
101+
context,
102+
context.GetSingletonType(SemIR::NamespaceType::SingletonInstId),
103+
SemIR::NameId::ForPackageName(cpp_package_id),
104+
SemIR::NameScopeId::Package,
105+
/*diagnose_duplicate_namespace=*/false,
106+
[&]() {
107+
return context.AddInst<SemIR::ImportCppDecl>(
108+
imports.front().node_id, {});
109+
})
110+
.name_scope_id;
111+
}
112+
113+
auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path,
114+
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
115+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs)
116+
-> void {
117+
if (imports.empty()) {
118+
return;
119+
}
120+
121+
auto [ast, ast_has_error] =
122+
GenerateAst(context, importing_file_path, imports, fs);
123+
124+
PackageNameId package_id = imports.front().package_id;
125+
CARBON_CHECK(
126+
llvm::all_of(imports, [&](const Parse::Tree::PackagingNames& import) {
127+
return import.package_id == package_id;
128+
}));
129+
auto name_scope_id = AddNamespace(context, package_id, imports);
130+
SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
131+
name_scope.set_is_closed_import(true);
132+
133+
if (ast_has_error) {
134+
name_scope.set_has_error();
135+
}
80136
}
81137

82138
} // namespace Carbon::Check

toolchain/check/import_cpp.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@
1212
namespace Carbon::Check {
1313

1414
// Generates a C++ header that includes the imported cpp files, parses it and
15-
// report errors and warnings.
16-
auto ImportCppFiles(
17-
Context& context, llvm::StringRef importing_file_path,
18-
llvm::ArrayRef<std::pair<llvm::StringRef, SemIRLoc>> imports,
19-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) -> void;
15+
// report errors and warnings. If successful, adds a `Cpp` namespace.
16+
auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path,
17+
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
18+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) -> void;
2019

2120
} // namespace Carbon::Check
2221

toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,18 @@ import Cpp library "\"foo.h\"";
5656
// CHECK:STDOUT:
5757
// CHECK:STDOUT: --- fail_import_cpp_library_file_with_quotes.carbon
5858
// CHECK:STDOUT:
59+
// CHECK:STDOUT: imports {
60+
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [template] {
61+
// CHECK:STDOUT: has_error
62+
// CHECK:STDOUT: }
63+
// CHECK:STDOUT: }
64+
// CHECK:STDOUT:
5965
// CHECK:STDOUT: file {
60-
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
66+
// CHECK:STDOUT: package: <namespace> = namespace [template] {
67+
// CHECK:STDOUT: .Cpp = imports.%Cpp
68+
// CHECK:STDOUT: }
69+
// CHECK:STDOUT: %Cpp.import_cpp = import_cpp {
70+
// CHECK:STDOUT: import Cpp "\"foo.h\""
71+
// CHECK:STDOUT: }
6172
// CHECK:STDOUT: }
6273
// CHECK:STDOUT:

0 commit comments

Comments
 (0)