Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions toolchain/check/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ cc_library(
"global_init.cpp",
"impl_lookup.cpp",
"import.cpp",
"import_cpp.cpp",
"import_ref.cpp",
"inst_block_stack.cpp",
"literal.cpp",
Expand All @@ -52,6 +53,7 @@ cc_library(
"global_init.h",
"impl_lookup.h",
"import.h",
"import_cpp.h",
"import_ref.h",
"inst_block_stack.h",
"keyword_modifier_set.h",
Expand Down Expand Up @@ -88,6 +90,8 @@ cc_library(
"//toolchain/sem_ir:formatter",
"//toolchain/sem_ir:inst",
"//toolchain/sem_ir:typed_insts",
"@llvm-project//clang:frontend",
"@llvm-project//clang:tooling",
"@llvm-project//llvm:Support",
],
)
Expand Down
42 changes: 32 additions & 10 deletions toolchain/check/check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,36 @@ static auto RenderImportKey(ImportKey import_key) -> std::string {
static auto TrackImport(Map<ImportKey, UnitAndImports*>& api_map,
Map<ImportKey, Parse::NodeId>* explicit_import_map,
UnitAndImports& unit_info,
Parse::Tree::PackagingNames import) -> void {
Parse::Tree::PackagingNames import, bool fuzzing)
-> void {
const auto& packaging = unit_info.parse_tree().packaging_decl();

IdentifierId file_package_id =
packaging ? packaging->names.package_id : IdentifierId::Invalid;
auto import_key = GetImportKey(unit_info, file_package_id, import);
const auto import_key = GetImportKey(unit_info, file_package_id, import);
const auto& [import_package_name, import_library_name] = import_key;

if (import_package_name == CppPackageName) {
if (import_library_name.empty()) {
CARBON_DIAGNOSTIC(CppInteropMissingLibrary, Error,
"`Cpp` import missing library");
unit_info.emitter.Emit(import.node_id, CppInteropMissingLibrary);
return;
}
if (fuzzing) {
// Clang is not crash-resilient.
CARBON_DIAGNOSTIC(CppInteropFuzzing, Error,
"`Cpp` import found during fuzzing");
unit_info.emitter.Emit(import.node_id, CppInteropFuzzing);
return;
}
unit_info.cpp_imports.push_back(import);
return;
}

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

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

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

Map<ImportKey, Parse::NodeId> explicit_import_map;
Expand All @@ -332,11 +353,12 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
TrackImport(api_map, &explicit_import_map, unit_info,
{.node_id = Parse::InvalidNodeId(),
.package_id = core_ident_id,
.library_id = prelude_id});
.library_id = prelude_id},
fuzzing);
}

for (const auto& import : unit_info.parse_tree().imports()) {
TrackImport(api_map, &explicit_import_map, unit_info, import);
TrackImport(api_map, &explicit_import_map, unit_info, import, fuzzing);
}

// If there were no imports, mark the file as ready to check for below.
Expand All @@ -350,7 +372,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
for (int check_index = 0;
check_index < static_cast<int>(ready_to_check.size()); ++check_index) {
auto* unit_info = ready_to_check[check_index];
CheckUnit(unit_info, units.size(), vlog_stream).Run();
CheckUnit(unit_info, units.size(), fs, vlog_stream).Run();
for (auto* incoming_import : unit_info->incoming_imports) {
--incoming_import->imports_remaining;
if (incoming_import->imports_remaining == 0) {
Expand Down Expand Up @@ -397,7 +419,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
// incomplete imports.
for (auto& unit_info : unit_infos) {
if (unit_info.imports_remaining > 0) {
CheckUnit(&unit_info, units.size(), vlog_stream).Run();
CheckUnit(&unit_info, units.size(), fs, vlog_stream).Run();
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion toolchain/check/check.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ struct Unit {
// Checks a group of parse trees. This will use imports to decide the order of
// checking.
auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
llvm::raw_ostream* vlog_stream) -> void;
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::raw_ostream* vlog_stream, bool fuzzing) -> void;

} // namespace Carbon::Check

Expand Down
37 changes: 37 additions & 0 deletions toolchain/check/check_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@

#include "toolchain/check/check_unit.h"

#include <string>

#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/base/pretty_stack_trace_function.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/handle.h"
#include "toolchain/check/impl.h"
#include "toolchain/check/import.h"
#include "toolchain/check/import_cpp.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/node_id_traversal.h"

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

CheckUnit::CheckUnit(UnitAndImports* unit_and_imports, int total_ir_count,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::raw_ostream* vlog_stream)
: unit_and_imports_(unit_and_imports),
total_ir_count_(total_ir_count),
fs_(std::move(fs)),
vlog_stream_(vlog_stream),
emitter_(*unit_and_imports_->unit->sem_ir_converter,
unit_and_imports_->err_tracker),
Expand Down Expand Up @@ -130,6 +138,7 @@ auto CheckUnit::InitPackageScopeAndImports() -> void {
ImportCurrentPackage(package_inst_id, namespace_type_id);
CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package);
ImportOtherPackages(namespace_type_id);
ImportCppPackages();
}

auto CheckUnit::CollectDirectImports(
Expand Down Expand Up @@ -325,6 +334,34 @@ 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;
}

if (imports.size() >= 2) {
context_.TODO(imports[1].node_id,
"multiple Cpp imports are not yet supported");
return;
}

const auto& import = imports.front();
llvm::StringRef filename =
unit_and_imports_->unit->value_stores->string_literal_values().Get(
import.library_id);

// TODO: Pass the import location so that diagnostics would point to it.
auto source_buffer = SourceBuffer::MakeFromFile(
*fs_, filename, unit_and_imports_->err_tracker);
if (!source_buffer) {
return;
}

ImportCppFile(context_, import.node_id, source_buffer->filename(),
source_buffer->text());
}

// 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)
Expand Down
10 changes: 9 additions & 1 deletion toolchain/check/check_unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ struct UnitAndImports {
// A map of the package names to the outgoing imports above.
Map<IdentifierId, int32_t> package_imports_map;

// List of the `import Cpp` imports.
llvm::SmallVector<Parse::Tree::PackagingNames> cpp_imports;

// The remaining number of imports which must be checked before this unit can
// be processed.
int32_t imports_remaining = 0;
Expand All @@ -98,6 +101,7 @@ struct UnitAndImports {
class CheckUnit {
public:
explicit CheckUnit(UnitAndImports* unit_and_imports, int total_ir_count,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::raw_ostream* vlog_stream);

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

// Imports all other packages (excluding the current package).
// 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
Expand All @@ -142,6 +149,7 @@ class CheckUnit {
UnitAndImports* unit_and_imports_;
// The number of IRs being checked in total.
int total_ir_count_;
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs_;
llvm::raw_ostream* vlog_stream_;

Context::DiagnosticEmitter emitter_;
Expand Down
57 changes: 57 additions & 0 deletions toolchain/check/import_cpp.cpp
Original file line number Diff line number Diff line change
@@ -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 "toolchain/check/import_cpp.h"

#include <memory>
#include <string>

#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "toolchain/check/context.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/diagnostics/format_providers.h"

namespace Carbon::Check {

auto ImportCppFile(Context& context, SemIRLoc loc, llvm::StringRef file_path,
llvm::StringRef code) -> void {
std::string diagnostics_str;
llvm::raw_string_ostream diagnostics_stream(diagnostics_str);

llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnostic_options(
new clang::DiagnosticOptions());
clang::TextDiagnosticPrinter diagnostics_consumer(diagnostics_stream,
diagnostic_options.get());
// TODO: Share compilation flags with ClangRunner.
auto ast = clang::tooling::buildASTFromCodeWithArgs(
code, {}, file_path, "clang-tool",
std::make_shared<clang::PCHContainerOperations>(),
clang::tooling::getClangStripDependencyFileAdjuster(),
clang::tooling::FileContentMappings(), &diagnostics_consumer);
// TODO: Implement and use a DynamicRecursiveASTVisitor to traverse the AST.
int num_errors = diagnostics_consumer.getNumErrors();
int num_warnings = diagnostics_consumer.getNumWarnings();
if (num_errors > 0) {
// TODO: Remove the warnings part when there are no warnings.
CARBON_DIAGNOSTIC(
CppInteropParseError, Error,
"{0} error{0:s} and {1} warning{1:s} in `Cpp` import `{2}`:\n{3}",
IntAsSelect, IntAsSelect, std::string, std::string);
context.emitter().Emit(loc, CppInteropParseError, num_errors, num_warnings,
file_path.str(), diagnostics_str);
} else if (num_warnings > 0) {
CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning,
"{0} warning{0:s} in `Cpp` import `{1}`:\n{2}",
IntAsSelect, std::string, std::string);
context.emitter().Emit(loc, CppInteropParseWarning, num_warnings,
file_path.str(), diagnostics_str);
}
}

} // namespace Carbon::Check
20 changes: 20 additions & 0 deletions toolchain/check/import_cpp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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_CHECK_IMPORT_CPP_H_
#define CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_

#include "llvm/ADT/StringRef.h"
#include "toolchain/check/context.h"
#include "toolchain/check/diagnostic_helpers.h"

namespace Carbon::Check {

// Parses the C++ code and report errors and warnings.
auto ImportCppFile(Context& context, SemIRLoc loc, llvm::StringRef file_path,
llvm::StringRef code) -> void;

} // namespace Carbon::Check

#endif // CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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/bad_import.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon

// --- fail_import_cpp.carbon

library "[[@TEST_NAME]]";

// CHECK:STDERR: fail_import_cpp.carbon:[[@LINE+4]]:1: error: `Cpp` import missing library [CppInteropMissingLibrary]
// CHECK:STDERR: import Cpp;
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
import Cpp;

// --- fail_import_cpp_library_empty.carbon

library "[[@TEST_NAME]]";

// CHECK:STDERR: fail_import_cpp_library_empty.carbon:[[@LINE+5]]:1: error: `Cpp` import missing library [CppInteropMissingLibrary]
// CHECK:STDERR: import Cpp library "";
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
// CHECK:STDERR: "foo.h": error: error opening file for read: No such file or directory [ErrorOpeningFile]
import Cpp library "";

// --- fail_import_cpp_library_file_with_quotes.carbon

library "[[@TEST_NAME]]";

import Cpp library "\"foo.h\"";

// CHECK:STDOUT: --- fail_import_cpp.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_import_cpp_library_empty.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_import_cpp_library_file_with_quotes.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
Loading
Loading