Skip to content

Commit 5b70a3e

Browse files
authored
Generate AST when importing a cpp file (#4790)
Ignore the AST and support a single Cpp import, for now. Report cpp compilation errors and warnings. Part of #4666
1 parent f5f6ae2 commit 5b70a3e

File tree

16 files changed

+481
-25
lines changed

16 files changed

+481
-25
lines changed

toolchain/check/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ cc_library(
2626
"global_init.cpp",
2727
"impl_lookup.cpp",
2828
"import.cpp",
29+
"import_cpp.cpp",
2930
"import_ref.cpp",
3031
"inst_block_stack.cpp",
3132
"literal.cpp",
@@ -52,6 +53,7 @@ cc_library(
5253
"global_init.h",
5354
"impl_lookup.h",
5455
"import.h",
56+
"import_cpp.h",
5557
"import_ref.h",
5658
"inst_block_stack.h",
5759
"keyword_modifier_set.h",
@@ -88,6 +90,8 @@ cc_library(
8890
"//toolchain/sem_ir:formatter",
8991
"//toolchain/sem_ir:inst",
9092
"//toolchain/sem_ir:typed_insts",
93+
"@llvm-project//clang:frontend",
94+
"@llvm-project//clang:tooling",
9195
"@llvm-project//llvm:Support",
9296
],
9397
)

toolchain/check/check.cpp

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,36 @@ static auto RenderImportKey(ImportKey import_key) -> std::string {
6161
static auto TrackImport(Map<ImportKey, UnitAndImports*>& api_map,
6262
Map<ImportKey, Parse::NodeId>* explicit_import_map,
6363
UnitAndImports& unit_info,
64-
Parse::Tree::PackagingNames import) -> void {
64+
Parse::Tree::PackagingNames import, bool fuzzing)
65+
-> void {
6566
const auto& packaging = unit_info.parse_tree().packaging_decl();
6667

6768
IdentifierId file_package_id =
6869
packaging ? packaging->names.package_id : IdentifierId::Invalid;
69-
auto import_key = GetImportKey(unit_info, file_package_id, import);
70+
const auto import_key = GetImportKey(unit_info, file_package_id, import);
71+
const auto& [import_package_name, import_library_name] = import_key;
72+
73+
if (import_package_name == CppPackageName) {
74+
if (import_library_name.empty()) {
75+
CARBON_DIAGNOSTIC(CppInteropMissingLibrary, Error,
76+
"`Cpp` import missing library");
77+
unit_info.emitter.Emit(import.node_id, CppInteropMissingLibrary);
78+
return;
79+
}
80+
if (fuzzing) {
81+
// Clang is not crash-resilient.
82+
CARBON_DIAGNOSTIC(CppInteropFuzzing, Error,
83+
"`Cpp` import found during fuzzing");
84+
unit_info.emitter.Emit(import.node_id, CppInteropFuzzing);
85+
return;
86+
}
87+
unit_info.cpp_imports.push_back(import);
88+
return;
89+
}
7090

7191
// True if the import has `Main` as the package name, even if it comes from
7292
// the file's packaging (diagnostics may differentiate).
73-
bool is_explicit_main = import_key.first == MainPackageName;
93+
bool is_explicit_main = import_package_name == MainPackageName;
7494

7595
// Explicit imports need more validation than implicit ones. We try to do
7696
// these in an order of imports that should be removed, followed by imports
@@ -185,7 +205,7 @@ static auto TrackImport(Map<ImportKey, UnitAndImports*>& api_map,
185205
} else {
186206
// The imported api is missing.
187207
package_imports.has_load_error = true;
188-
if (!explicit_import_map && import_key.first == CppPackageName) {
208+
if (!explicit_import_map && import_package_name == CppPackageName) {
189209
// Don't diagnose the implicit import in `impl package Cpp`, because we'll
190210
// have diagnosed the use of `Cpp` in the declaration.
191211
return;
@@ -295,7 +315,8 @@ static auto BuildApiMapAndDiagnosePackaging(
295315
}
296316

297317
auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
298-
llvm::raw_ostream* vlog_stream) -> void {
318+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
319+
llvm::raw_ostream* vlog_stream, bool fuzzing) -> void {
299320
// UnitAndImports is big due to its SmallVectors, so we default to 0 on the
300321
// stack.
301322
llvm::SmallVector<UnitAndImports, 0> unit_infos;
@@ -316,7 +337,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
316337
// An `impl` has an implicit import of its `api`.
317338
auto implicit_names = packaging->names;
318339
implicit_names.package_id = IdentifierId::Invalid;
319-
TrackImport(api_map, nullptr, unit_info, implicit_names);
340+
TrackImport(api_map, nullptr, unit_info, implicit_names, fuzzing);
320341
}
321342

322343
Map<ImportKey, Parse::NodeId> explicit_import_map;
@@ -332,11 +353,12 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
332353
TrackImport(api_map, &explicit_import_map, unit_info,
333354
{.node_id = Parse::InvalidNodeId(),
334355
.package_id = core_ident_id,
335-
.library_id = prelude_id});
356+
.library_id = prelude_id},
357+
fuzzing);
336358
}
337359

338360
for (const auto& import : unit_info.parse_tree().imports()) {
339-
TrackImport(api_map, &explicit_import_map, unit_info, import);
361+
TrackImport(api_map, &explicit_import_map, unit_info, import, fuzzing);
340362
}
341363

342364
// If there were no imports, mark the file as ready to check for below.
@@ -350,7 +372,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
350372
for (int check_index = 0;
351373
check_index < static_cast<int>(ready_to_check.size()); ++check_index) {
352374
auto* unit_info = ready_to_check[check_index];
353-
CheckUnit(unit_info, units.size(), vlog_stream).Run();
375+
CheckUnit(unit_info, units.size(), fs, vlog_stream).Run();
354376
for (auto* incoming_import : unit_info->incoming_imports) {
355377
--incoming_import->imports_remaining;
356378
if (incoming_import->imports_remaining == 0) {
@@ -397,7 +419,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
397419
// incomplete imports.
398420
for (auto& unit_info : unit_infos) {
399421
if (unit_info.imports_remaining > 0) {
400-
CheckUnit(&unit_info, units.size(), vlog_stream).Run();
422+
CheckUnit(&unit_info, units.size(), fs, vlog_stream).Run();
401423
}
402424
}
403425
}

toolchain/check/check.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ struct Unit {
3737
// Checks a group of parse trees. This will use imports to decide the order of
3838
// checking.
3939
auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
40-
llvm::raw_ostream* vlog_stream) -> void;
40+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
41+
llvm::raw_ostream* vlog_stream, bool fuzzing) -> void;
4142

4243
} // namespace Carbon::Check
4344

toolchain/check/check_unit.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44

55
#include "toolchain/check/check_unit.h"
66

7+
#include <string>
8+
9+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
10+
#include "llvm/ADT/StringRef.h"
11+
#include "llvm/Support/VirtualFileSystem.h"
712
#include "toolchain/base/kind_switch.h"
813
#include "toolchain/base/pretty_stack_trace_function.h"
914
#include "toolchain/check/generic.h"
1015
#include "toolchain/check/handle.h"
1116
#include "toolchain/check/impl.h"
1217
#include "toolchain/check/import.h"
18+
#include "toolchain/check/import_cpp.h"
1319
#include "toolchain/check/import_ref.h"
1420
#include "toolchain/check/node_id_traversal.h"
1521

@@ -29,9 +35,11 @@ static auto GetImportedIRCount(UnitAndImports* unit_and_imports) -> int {
2935
}
3036

3137
CheckUnit::CheckUnit(UnitAndImports* unit_and_imports, int total_ir_count,
38+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
3239
llvm::raw_ostream* vlog_stream)
3340
: unit_and_imports_(unit_and_imports),
3441
total_ir_count_(total_ir_count),
42+
fs_(std::move(fs)),
3543
vlog_stream_(vlog_stream),
3644
emitter_(*unit_and_imports_->unit->sem_ir_converter,
3745
unit_and_imports_->err_tracker),
@@ -130,6 +138,7 @@ auto CheckUnit::InitPackageScopeAndImports() -> void {
130138
ImportCurrentPackage(package_inst_id, namespace_type_id);
131139
CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package);
132140
ImportOtherPackages(namespace_type_id);
141+
ImportCppPackages();
133142
}
134143

135144
auto CheckUnit::CollectDirectImports(
@@ -325,6 +334,34 @@ auto CheckUnit::ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void {
325334
}
326335
}
327336

337+
auto CheckUnit::ImportCppPackages() -> void {
338+
const auto& imports = unit_and_imports_->cpp_imports;
339+
if (imports.empty()) {
340+
return;
341+
}
342+
343+
if (imports.size() >= 2) {
344+
context_.TODO(imports[1].node_id,
345+
"multiple Cpp imports are not yet supported");
346+
return;
347+
}
348+
349+
const auto& import = imports.front();
350+
llvm::StringRef filename =
351+
unit_and_imports_->unit->value_stores->string_literal_values().Get(
352+
import.library_id);
353+
354+
// TODO: Pass the import location so that diagnostics would point to it.
355+
auto source_buffer = SourceBuffer::MakeFromFile(
356+
*fs_, filename, unit_and_imports_->err_tracker);
357+
if (!source_buffer) {
358+
return;
359+
}
360+
361+
ImportCppFile(context_, import.node_id, source_buffer->filename(),
362+
source_buffer->text());
363+
}
364+
328365
// Loops over all nodes in the tree. On some errors, this may return early,
329366
// for example if an unrecoverable state is encountered.
330367
// NOLINTNEXTLINE(readability-function-size)

toolchain/check/check_unit.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ struct UnitAndImports {
7474
// A map of the package names to the outgoing imports above.
7575
Map<IdentifierId, int32_t> package_imports_map;
7676

77+
// List of the `import Cpp` imports.
78+
llvm::SmallVector<Parse::Tree::PackagingNames> cpp_imports;
79+
7780
// The remaining number of imports which must be checked before this unit can
7881
// be processed.
7982
int32_t imports_remaining = 0;
@@ -98,6 +101,7 @@ struct UnitAndImports {
98101
class CheckUnit {
99102
public:
100103
explicit CheckUnit(UnitAndImports* unit_and_imports, int total_ir_count,
104+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
101105
llvm::raw_ostream* vlog_stream);
102106

103107
// Produces and checks the IR for the provided unit.
@@ -125,9 +129,12 @@ class CheckUnit {
125129
auto ImportCurrentPackage(SemIR::InstId package_inst_id,
126130
SemIR::TypeId namespace_type_id) -> void;
127131

128-
// Imports all other packages (excluding the current package).
132+
// Imports all other Carbon packages (excluding the current package).
129133
auto ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void;
130134

135+
// Imports all C++ packages.
136+
auto ImportCppPackages() -> void;
137+
131138
// Checks that each required definition is available. If the definition can be
132139
// generated by resolving a specific, does so, otherwise emits a diagnostic
133140
// for each declaration in context.definitions_required() that doesn't have a
@@ -142,6 +149,7 @@ class CheckUnit {
142149
UnitAndImports* unit_and_imports_;
143150
// The number of IRs being checked in total.
144151
int total_ir_count_;
152+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs_;
145153
llvm::raw_ostream* vlog_stream_;
146154

147155
Context::DiagnosticEmitter emitter_;

toolchain/check/import_cpp.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "toolchain/check/import_cpp.h"
6+
7+
#include <memory>
8+
#include <string>
9+
10+
#include "clang/Frontend/TextDiagnosticPrinter.h"
11+
#include "clang/Tooling/Tooling.h"
12+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
13+
#include "llvm/ADT/StringRef.h"
14+
#include "llvm/Support/raw_ostream.h"
15+
#include "toolchain/check/context.h"
16+
#include "toolchain/check/diagnostic_helpers.h"
17+
#include "toolchain/diagnostics/diagnostic.h"
18+
#include "toolchain/diagnostics/format_providers.h"
19+
20+
namespace Carbon::Check {
21+
22+
auto ImportCppFile(Context& context, SemIRLoc loc, llvm::StringRef file_path,
23+
llvm::StringRef code) -> void {
24+
std::string diagnostics_str;
25+
llvm::raw_string_ostream diagnostics_stream(diagnostics_str);
26+
27+
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnostic_options(
28+
new clang::DiagnosticOptions());
29+
clang::TextDiagnosticPrinter diagnostics_consumer(diagnostics_stream,
30+
diagnostic_options.get());
31+
// TODO: Share compilation flags with ClangRunner.
32+
auto ast = clang::tooling::buildASTFromCodeWithArgs(
33+
code, {}, file_path, "clang-tool",
34+
std::make_shared<clang::PCHContainerOperations>(),
35+
clang::tooling::getClangStripDependencyFileAdjuster(),
36+
clang::tooling::FileContentMappings(), &diagnostics_consumer);
37+
// TODO: Implement and use a DynamicRecursiveASTVisitor to traverse the AST.
38+
int num_errors = diagnostics_consumer.getNumErrors();
39+
int num_warnings = diagnostics_consumer.getNumWarnings();
40+
if (num_errors > 0) {
41+
// TODO: Remove the warnings part when there are no warnings.
42+
CARBON_DIAGNOSTIC(
43+
CppInteropParseError, Error,
44+
"{0} error{0:s} and {1} warning{1:s} in `Cpp` import `{2}`:\n{3}",
45+
IntAsSelect, IntAsSelect, std::string, std::string);
46+
context.emitter().Emit(loc, CppInteropParseError, num_errors, num_warnings,
47+
file_path.str(), diagnostics_str);
48+
} else if (num_warnings > 0) {
49+
CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning,
50+
"{0} warning{0:s} in `Cpp` import `{1}`:\n{2}",
51+
IntAsSelect, std::string, std::string);
52+
context.emitter().Emit(loc, CppInteropParseWarning, num_warnings,
53+
file_path.str(), diagnostics_str);
54+
}
55+
}
56+
57+
} // namespace Carbon::Check

toolchain/check/import_cpp.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#ifndef CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_
6+
#define CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_
7+
8+
#include "llvm/ADT/StringRef.h"
9+
#include "toolchain/check/context.h"
10+
#include "toolchain/check/diagnostic_helpers.h"
11+
12+
namespace Carbon::Check {
13+
14+
// Parses the C++ code and report errors and warnings.
15+
auto ImportCppFile(Context& context, SemIRLoc loc, llvm::StringRef file_path,
16+
llvm::StringRef code) -> void;
17+
18+
} // namespace Carbon::Check
19+
20+
#endif // CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// AUTOUPDATE
6+
// TIP: To test this file alone, run:
7+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon
8+
// TIP: To dump output, run:
9+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon
10+
11+
// --- fail_import_cpp.carbon
12+
13+
library "[[@TEST_NAME]]";
14+
15+
// CHECK:STDERR: fail_import_cpp.carbon:[[@LINE+4]]:1: error: `Cpp` import missing library [CppInteropMissingLibrary]
16+
// CHECK:STDERR: import Cpp;
17+
// CHECK:STDERR: ^~~~~~
18+
// CHECK:STDERR:
19+
import Cpp;
20+
21+
// --- fail_import_cpp_library_empty.carbon
22+
23+
library "[[@TEST_NAME]]";
24+
25+
// CHECK:STDERR: fail_import_cpp_library_empty.carbon:[[@LINE+5]]:1: error: `Cpp` import missing library [CppInteropMissingLibrary]
26+
// CHECK:STDERR: import Cpp library "";
27+
// CHECK:STDERR: ^~~~~~
28+
// CHECK:STDERR:
29+
// CHECK:STDERR: "foo.h": error: error opening file for read: No such file or directory [ErrorOpeningFile]
30+
import Cpp library "";
31+
32+
// --- fail_import_cpp_library_file_with_quotes.carbon
33+
34+
library "[[@TEST_NAME]]";
35+
36+
import Cpp library "\"foo.h\"";
37+
38+
// CHECK:STDOUT: --- fail_import_cpp.carbon
39+
// CHECK:STDOUT:
40+
// CHECK:STDOUT: file {
41+
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
42+
// CHECK:STDOUT: }
43+
// CHECK:STDOUT:
44+
// CHECK:STDOUT: --- fail_import_cpp_library_empty.carbon
45+
// CHECK:STDOUT:
46+
// CHECK:STDOUT: file {
47+
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
48+
// CHECK:STDOUT: }
49+
// CHECK:STDOUT:
50+
// CHECK:STDOUT: --- fail_import_cpp_library_file_with_quotes.carbon
51+
// CHECK:STDOUT:
52+
// CHECK:STDOUT: file {
53+
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
54+
// CHECK:STDOUT: }
55+
// CHECK:STDOUT:

0 commit comments

Comments
 (0)