mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:20:11 +01:00
Add a flag to build a single ASTContext shared across all compilations (#7567)
Instead of building one Clang `ASTContext` per compilation, the `--share-cpp-ast` flag causes us to build a single `ASTContext` and share it across all contexts. One new abstraction is added: `CppDomain` represents the Carbon-side view of a Clang AST that might be shared across multiple `SemIR::File`s. This object owns the Clang instance and the AST. For now, we have no isolation between the C++ state exposed to different Carbon compilations, and we have no multiplexing of generated LLVM IR from C++ into different Carbon compilations, so the mode is not usable yet. The plan is to keep it behind a flag until it's ready. Assisted-by: Gemini via Antigravity
This commit is contained in:
@@ -7,11 +7,16 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Sema/MultiplexExternalSemaSource.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "common/check.h"
|
||||
#include "common/map.h"
|
||||
#include "common/pretty_stack_trace_function.h"
|
||||
#include "toolchain/check/check_unit.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/cpp/generate_ast.h"
|
||||
#include "toolchain/check/cpp/import.h"
|
||||
#include "toolchain/check/diagnostic_emitter.h"
|
||||
#include "toolchain/check/diagnostic_helpers.h"
|
||||
@@ -23,6 +28,7 @@
|
||||
#include "toolchain/parse/tree.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/formatter.h"
|
||||
#include "toolchain/sem_ir/read_only_ast_source.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
@@ -492,14 +498,42 @@ auto CheckParseTrees(
|
||||
}
|
||||
}
|
||||
|
||||
// C++ domains used across files. When compiling with a single ASTContext
|
||||
// (`options.share_cpp_ast`), there is only a single shared domain.
|
||||
llvm::SmallVector<std::shared_ptr<CppDomain>> cpp_domains;
|
||||
if (options.share_cpp_ast) {
|
||||
// TODO: Remove dependence on properties of the first unit here.
|
||||
auto shared_cpp_domain = InitializeCppDomain(
|
||||
unit_infos.front().err_tracker,
|
||||
unit_infos.front().unit->sem_ir->filename(), fs,
|
||||
unit_infos.front().unit->llvm_context, clang_invocation);
|
||||
if (shared_cpp_domain) {
|
||||
cpp_domains.push_back(shared_cpp_domain);
|
||||
for (auto& target_info : unit_infos) {
|
||||
target_info.cpp_domain = shared_cpp_domain;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto& unit_info : unit_infos) {
|
||||
if (unit_info.cpp_imports.empty()) {
|
||||
continue;
|
||||
}
|
||||
unit_info.cpp_domain = InitializeCppDomain(
|
||||
unit_info.err_tracker, unit_info.unit->sem_ir->filename(), fs,
|
||||
unit_info.unit->llvm_context, clang_invocation);
|
||||
if (unit_info.cpp_domain) {
|
||||
cpp_domains.push_back(unit_info.cpp_domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check everything with no dependencies. Earlier entries with dependencies
|
||||
// will be checked as soon as all their dependencies have been checked.
|
||||
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, &tree_and_subtrees_getters, fs,
|
||||
unit_info->unit->llvm_context, clang_invocation,
|
||||
options.vlog_stream, options.mangle_string_fingerprint)
|
||||
CheckUnit(unit_info, &tree_and_subtrees_getters, options.vlog_stream,
|
||||
options.mangle_string_fingerprint)
|
||||
.Run();
|
||||
for (auto* incoming_import : unit_info->incoming_imports) {
|
||||
--incoming_import->imports_remaining;
|
||||
@@ -547,14 +581,18 @@ auto CheckParseTrees(
|
||||
// incomplete imports.
|
||||
for (auto& unit_info : unit_infos) {
|
||||
if (unit_info.imports_remaining > 0) {
|
||||
CheckUnit(&unit_info, &tree_and_subtrees_getters, fs,
|
||||
unit_info.unit->llvm_context, clang_invocation,
|
||||
options.vlog_stream, options.mangle_string_fingerprint)
|
||||
CheckUnit(&unit_info, &tree_and_subtrees_getters, options.vlog_stream,
|
||||
options.mangle_string_fingerprint)
|
||||
.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize all C++ domains at the end of checking.
|
||||
for (const auto& domain : cpp_domains) {
|
||||
FinalizeCppDomain(*domain);
|
||||
}
|
||||
|
||||
MaybeDumpSemIR(units, tree_and_subtrees_getters, options);
|
||||
MaybeDumpCppAST(units, options);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ struct Unit {
|
||||
int total_ir_count;
|
||||
};
|
||||
|
||||
struct CppDomain;
|
||||
|
||||
struct CheckParseTreesOptions {
|
||||
// Options must be set individually, not through initialization.
|
||||
explicit CheckParseTreesOptions() = default;
|
||||
@@ -79,6 +81,9 @@ struct CheckParseTreesOptions {
|
||||
// Whether to use the string form of the fingerprint from mangling instead of
|
||||
// the hash form.
|
||||
bool mangle_string_fingerprint = false;
|
||||
|
||||
// Whether to share a single Clang ASTContext across all files.
|
||||
bool share_cpp_ast = false;
|
||||
};
|
||||
|
||||
// Checks a group of parse trees. This will use imports to decide the order of
|
||||
|
||||
@@ -61,16 +61,10 @@ static auto GetImportedIRCount(UnitAndImports* unit_and_imports) -> int {
|
||||
CheckUnit::CheckUnit(
|
||||
UnitAndImports* unit_and_imports,
|
||||
const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> clang_invocation,
|
||||
llvm::raw_ostream* vlog_stream, bool mangle_string_fingerprint)
|
||||
: unit_and_imports_(unit_and_imports),
|
||||
tree_and_subtrees_getter_(tree_and_subtrees_getters->Get(
|
||||
unit_and_imports->unit->sem_ir->check_ir_id())),
|
||||
fs_(std::move(fs)),
|
||||
llvm_context_(llvm_context),
|
||||
clang_invocation_(std::move(clang_invocation)),
|
||||
emitter_(&unit_and_imports_->err_tracker, tree_and_subtrees_getters,
|
||||
unit_and_imports_->unit->sem_ir),
|
||||
context_(&emitter_, tree_and_subtrees_getter_,
|
||||
@@ -165,10 +159,8 @@ auto CheckUnit::InitPackageScopeAndImports() -> void {
|
||||
CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package);
|
||||
ImportOtherPackages(namespace_type_id);
|
||||
|
||||
const auto& cpp_imports = unit_and_imports_->cpp_imports;
|
||||
if (!cpp_imports.empty()) {
|
||||
ImportCpp(context_, cpp_imports, fs_, llvm_context_, clang_invocation_);
|
||||
}
|
||||
ImportCpp(context_, unit_and_imports_->cpp_imports,
|
||||
unit_and_imports_->cpp_domain.get());
|
||||
}
|
||||
|
||||
auto CheckUnit::CollectDirectImports(
|
||||
|
||||
@@ -20,6 +20,7 @@ class CompilerInvocation;
|
||||
namespace Carbon::Check {
|
||||
|
||||
struct UnitAndImports;
|
||||
struct CppDomain;
|
||||
|
||||
// A file's imports corresponding to a single package, for
|
||||
// `UnitAndImports::package_imports`.
|
||||
@@ -101,6 +102,9 @@ struct UnitAndImports {
|
||||
// List of the `import Cpp` imports.
|
||||
llvm::SmallVector<Parse::Tree::PackagingNames> cpp_imports;
|
||||
|
||||
// The C++ domain for this unit.
|
||||
std::shared_ptr<CppDomain> cpp_domain;
|
||||
|
||||
// The remaining number of imports which must be checked before this unit can
|
||||
// be processed.
|
||||
int32_t imports_remaining = 0;
|
||||
@@ -129,9 +133,6 @@ class CheckUnit {
|
||||
explicit CheckUnit(
|
||||
UnitAndImports* unit_and_imports,
|
||||
const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> clang_invocation,
|
||||
llvm::raw_ostream* vlog_stream, bool mangle_string_fingerprint = false);
|
||||
|
||||
// Produces and checks the IR for the provided unit.
|
||||
@@ -192,9 +193,6 @@ class CheckUnit {
|
||||
|
||||
UnitAndImports* unit_and_imports_;
|
||||
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_;
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs_;
|
||||
llvm::LLVMContext* llvm_context_;
|
||||
std::shared_ptr<clang::CompilerInvocation> clang_invocation_;
|
||||
|
||||
DiagnosticEmitter emitter_;
|
||||
Context context_;
|
||||
|
||||
@@ -11,18 +11,15 @@
|
||||
namespace Carbon::Check {
|
||||
|
||||
CppContext::CppContext(clang::CompilerInstance& instance,
|
||||
std::unique_ptr<clang::Parser> parser)
|
||||
std::shared_ptr<clang::Parser> parser,
|
||||
std::unique_ptr<CppDiagnosticListener> listener)
|
||||
: ast_context_(&instance.getASTContext()),
|
||||
sema_(&instance.getSema()),
|
||||
parser_(std::move(parser)) {}
|
||||
parser_(std::move(parser)),
|
||||
diagnostic_listener_(std::move(listener)) {}
|
||||
|
||||
CppContext::~CppContext() = default;
|
||||
|
||||
auto CppContext::set_diagnostic_listener(
|
||||
std::unique_ptr<CppDiagnosticListener> listener) -> void {
|
||||
diagnostic_listener_ = std::move(listener);
|
||||
}
|
||||
|
||||
auto CppContext::clang_mangle_context() -> clang::MangleContext& {
|
||||
if (!clang_mangle_context_) {
|
||||
clang_mangle_context_.reset(ast_context().createMangleContext());
|
||||
|
||||
@@ -31,12 +31,14 @@ namespace Carbon::Check {
|
||||
class CppContext {
|
||||
public:
|
||||
explicit CppContext(clang::CompilerInstance& instance,
|
||||
std::unique_ptr<clang::Parser> parser);
|
||||
std::shared_ptr<clang::Parser> parser,
|
||||
std::unique_ptr<CppDiagnosticListener> listener);
|
||||
~CppContext();
|
||||
|
||||
auto ast_context() -> clang::ASTContext& { return *ast_context_; }
|
||||
auto sema() -> clang::Sema& { return *sema_; }
|
||||
auto parser() -> clang::Parser& { return *parser_; }
|
||||
auto parser_ptr() const -> std::shared_ptr<clang::Parser> { return parser_; }
|
||||
|
||||
auto clang_mangle_context() -> clang::MangleContext&;
|
||||
|
||||
@@ -51,12 +53,6 @@ class CppContext {
|
||||
placement_new_decl_ = decl;
|
||||
}
|
||||
|
||||
auto diagnostic_listener() -> CppDiagnosticListener* {
|
||||
return diagnostic_listener_.get();
|
||||
}
|
||||
auto set_diagnostic_listener(std::unique_ptr<CppDiagnosticListener> listener)
|
||||
-> void;
|
||||
|
||||
private:
|
||||
// The Clang AST context.
|
||||
clang::ASTContext* ast_context_;
|
||||
@@ -65,7 +61,7 @@ class CppContext {
|
||||
clang::Sema* sema_;
|
||||
|
||||
// The Clang parser.
|
||||
std::unique_ptr<clang::Parser> parser_;
|
||||
std::shared_ptr<clang::Parser> parser_;
|
||||
|
||||
// Per-Carbon-file start locations for corresponding Clang source buffers.
|
||||
// Owned and managed by code in location.cpp.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/CompilerInvocation.h"
|
||||
#include "clang/Frontend/TextDiagnostic.h"
|
||||
#include "common/check.h"
|
||||
@@ -25,6 +26,34 @@ namespace Carbon::Check {
|
||||
|
||||
class CarbonClangDiagnosticConsumer;
|
||||
|
||||
// A diagnostic emitter that maps Clang SourceLocations to Carbon diagnostic
|
||||
// locations.
|
||||
class ClangLocDiagnosticEmitter
|
||||
: public Diagnostics::Emitter<clang::SourceLocation> {
|
||||
public:
|
||||
explicit ClangLocDiagnosticEmitter(Diagnostics::Consumer* consumer,
|
||||
const clang::SourceManager* source_manager)
|
||||
: Emitter(consumer), source_manager_(source_manager) {}
|
||||
|
||||
protected:
|
||||
auto ConvertLoc(clang::SourceLocation loc, ContextFnT /*context_fn*/) const
|
||||
-> Diagnostics::ConvertedLoc override {
|
||||
Diagnostics::Loc result_loc;
|
||||
if (source_manager_ && loc.isValid()) {
|
||||
clang::PresumedLoc presumed_loc = source_manager_->getPresumedLoc(loc);
|
||||
if (presumed_loc.isValid()) {
|
||||
result_loc.filename = presumed_loc.getFilename();
|
||||
result_loc.line_number = presumed_loc.getLine();
|
||||
result_loc.column_number = presumed_loc.getColumn();
|
||||
}
|
||||
}
|
||||
return {.loc = result_loc, .last_byte_offset = -1};
|
||||
}
|
||||
|
||||
private:
|
||||
const clang::SourceManager* source_manager_;
|
||||
};
|
||||
|
||||
// Returns the diagnostic to use for a given Clang diagnostic level.
|
||||
static auto GetDiagnostic(clang::DiagnosticsEngine::Level level)
|
||||
-> const Diagnostics::DiagnosticBase<std::string>& {
|
||||
@@ -56,27 +85,30 @@ static auto GetDiagnostic(clang::DiagnosticsEngine::Level level)
|
||||
// Diagnostics::Consumer when no Carbon Context is active.
|
||||
class FallbackDiagnosticListener : public CppDiagnosticListener {
|
||||
public:
|
||||
explicit FallbackDiagnosticListener(CarbonClangDiagnosticConsumer& consumer,
|
||||
Diagnostics::Consumer& next_consumer)
|
||||
: CppDiagnosticListener(consumer), next_consumer_(&next_consumer) {}
|
||||
|
||||
~FallbackDiagnosticListener() override;
|
||||
explicit FallbackDiagnosticListener(
|
||||
CarbonClangDiagnosticConsumer& clang_consumer,
|
||||
Diagnostics::Consumer& carbon_consumer)
|
||||
: CppDiagnosticListener(clang_consumer),
|
||||
carbon_consumer_(&carbon_consumer) {}
|
||||
|
||||
auto EmitDiagnostics(llvm::ArrayRef<Diagnostic> diags) -> void override {
|
||||
if (diags.empty()) {
|
||||
return;
|
||||
}
|
||||
Diagnostics::NoLocEmitter emitter(next_consumer_);
|
||||
ClangLocDiagnosticEmitter emitter(carbon_consumer_,
|
||||
diags[0].source_manager);
|
||||
for (size_t i = 0; i != diags.size(); ++i) {
|
||||
const Diagnostic& info = diags[i];
|
||||
auto builder =
|
||||
emitter.Build(nullptr, GetDiagnostic(info.level), info.message);
|
||||
emitter.Build(info.location, GetDiagnostic(info.level), info.message);
|
||||
builder.OverrideSnippet(info.snippet);
|
||||
for (; i + 1 < diags.size() &&
|
||||
diags[i + 1].level == clang::DiagnosticsEngine::Note;
|
||||
++i) {
|
||||
const Diagnostic& note_info = diags[i + 1];
|
||||
builder.Note(nullptr, GetDiagnostic(note_info.level), note_info.message)
|
||||
builder
|
||||
.Note(note_info.location, GetDiagnostic(note_info.level),
|
||||
note_info.message)
|
||||
.OverrideSnippet(note_info.snippet);
|
||||
}
|
||||
builder.Emit();
|
||||
@@ -84,7 +116,7 @@ class FallbackDiagnosticListener : public CppDiagnosticListener {
|
||||
}
|
||||
|
||||
private:
|
||||
Diagnostics::Consumer* next_consumer_;
|
||||
Diagnostics::Consumer* carbon_consumer_;
|
||||
};
|
||||
|
||||
// A listener that converts Clang diagnostics to Carbon diagnostics using a
|
||||
@@ -95,8 +127,6 @@ class ContextDiagnosticListener : public CppDiagnosticListener {
|
||||
Context& context)
|
||||
: CppDiagnosticListener(consumer), context_(&context) {}
|
||||
|
||||
~ContextDiagnosticListener() override;
|
||||
|
||||
auto EmitDiagnostics(llvm::ArrayRef<Diagnostic> diags) -> void override {
|
||||
if (diags.empty()) {
|
||||
return;
|
||||
@@ -213,8 +243,11 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
|
||||
diag_level, message, info.getRanges(), info.getFixItHints());
|
||||
}
|
||||
|
||||
const clang::SourceManager* source_manager =
|
||||
info.hasSourceManager() ? &info.getSourceManager() : nullptr;
|
||||
diagnostic_infos_.push_back({.level = diag_level,
|
||||
.location = info.getLocation(),
|
||||
.source_manager = source_manager,
|
||||
.message = message.str().str(),
|
||||
.snippet = snippet_stream.TakeStr()});
|
||||
}
|
||||
@@ -257,12 +290,6 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
|
||||
std::shared_ptr<clang::CompilerInvocation> invocation_;
|
||||
};
|
||||
|
||||
FallbackDiagnosticListener::~FallbackDiagnosticListener() {
|
||||
consumer().Flush();
|
||||
}
|
||||
|
||||
ContextDiagnosticListener::~ContextDiagnosticListener() { consumer().Flush(); }
|
||||
|
||||
CppDiagnosticListener::CppDiagnosticListener(
|
||||
CarbonClangDiagnosticConsumer& consumer)
|
||||
: consumer_(&consumer) {
|
||||
@@ -281,6 +308,10 @@ auto MakeDiagnosticConsumer(
|
||||
std::move(invocation));
|
||||
}
|
||||
|
||||
auto FlushDiagnosticConsumer(clang::DiagnosticConsumer& consumer) -> void {
|
||||
static_cast<CarbonClangDiagnosticConsumer&>(consumer).Flush();
|
||||
}
|
||||
|
||||
auto MakeContextDiagnosticListener(clang::DiagnosticConsumer& consumer,
|
||||
Context& context)
|
||||
-> std::unique_ptr<CppDiagnosticListener> {
|
||||
|
||||
@@ -29,6 +29,10 @@ auto MakeDiagnosticConsumer(
|
||||
std::shared_ptr<clang::CompilerInvocation> invocation)
|
||||
-> std::unique_ptr<clang::DiagnosticConsumer>;
|
||||
|
||||
// Flushes any pending diagnostics in the given consumer, which must have been
|
||||
// created by `MakeDiagnosticConsumer`.
|
||||
auto FlushDiagnosticConsumer(clang::DiagnosticConsumer& consumer) -> void;
|
||||
|
||||
// Creates a diagnostic listener attached to the given Carbon context. The
|
||||
// returned listener must not outlive the context.
|
||||
auto MakeContextDiagnosticListener(clang::DiagnosticConsumer& consumer,
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace clang {
|
||||
class SourceManager;
|
||||
} // namespace clang
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
class CarbonClangDiagnosticConsumer;
|
||||
@@ -25,6 +29,7 @@ class CppDiagnosticListener {
|
||||
struct Diagnostic {
|
||||
clang::DiagnosticsEngine::Level level;
|
||||
clang::SourceLocation location;
|
||||
const clang::SourceManager* source_manager = nullptr;
|
||||
std::string message;
|
||||
std::string snippet;
|
||||
};
|
||||
|
||||
@@ -137,7 +137,9 @@ class CarbonExternalASTSource : public SemIR::ReadOnlyASTSource {
|
||||
explicit CarbonExternalASTSource(Context* context)
|
||||
: ReadOnlyASTSource(context->sem_ir()), context_(context) {}
|
||||
|
||||
auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override;
|
||||
// Builds the top-level C++ namespace `Carbon` and adds it to the translation
|
||||
// unit.
|
||||
auto BuildCarbonNamespace() -> void;
|
||||
|
||||
// Look up decls for `decl_name` inside `decl_context`, adding the decls to
|
||||
// `decl_context`. Returns true if any decls were added.
|
||||
@@ -178,10 +180,6 @@ class CarbonExternalASTSource : public SemIR::ReadOnlyASTSource {
|
||||
}
|
||||
|
||||
private:
|
||||
// Builds the top-level C++ namespace `Carbon` and adds it to the translation
|
||||
// unit.
|
||||
auto BuildCarbonNamespace() -> void;
|
||||
|
||||
// Map a Carbon entity to a Clang NamedDecl. Returns null if the entity cannot
|
||||
// currently be represented in C++.
|
||||
auto MapInstIdToClangDeclOrType(LookupResult lookup)
|
||||
@@ -218,11 +216,6 @@ char CarbonExternalASTSource::id;
|
||||
|
||||
} // namespace
|
||||
|
||||
void CarbonExternalASTSource::StartTranslationUnit(
|
||||
clang::ASTConsumer* /*Consumer*/) {
|
||||
BuildCarbonNamespace();
|
||||
}
|
||||
|
||||
auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup)
|
||||
-> std::variant<clang::NamedDecl*, clang::QualType> {
|
||||
auto target_inst_id = lookup.scope_result.target_inst_id();
|
||||
@@ -321,16 +314,24 @@ auto CarbonExternalASTSource::BuildCarbonNamespace() -> void {
|
||||
auto& ast_context = context_->ast_context();
|
||||
auto* identifier = &ast_context.Idents.get(carbon_namespace_name);
|
||||
|
||||
// Create the namespace and add it to the translation unit scope.
|
||||
auto* decl_context = ast_context.getTranslationUnitDecl();
|
||||
auto* carbon_cpp_namespace = clang::NamespaceDecl::Create(
|
||||
ast_context, decl_context, /*Inline=*/false, clang::SourceLocation(),
|
||||
clang::SourceLocation(), identifier, /*PrevDecl=*/nullptr,
|
||||
/*Nested=*/false);
|
||||
decl_context->addDecl(carbon_cpp_namespace);
|
||||
|
||||
// We provide custom lookup results within this namespace.
|
||||
carbon_cpp_namespace->setHasExternalVisibleStorage();
|
||||
// Check if it already exists.
|
||||
clang::NamespaceDecl* carbon_cpp_namespace = nullptr;
|
||||
auto lookup_result = decl_context->lookup(identifier);
|
||||
if (!lookup_result.empty()) {
|
||||
carbon_cpp_namespace = cast<clang::NamespaceDecl>(lookup_result.front());
|
||||
} else {
|
||||
// Create it if it doesn't exist.
|
||||
carbon_cpp_namespace = clang::NamespaceDecl::Create(
|
||||
ast_context, decl_context, /*Inline=*/false, clang::SourceLocation(),
|
||||
clang::SourceLocation(), identifier, /*PrevDecl=*/nullptr,
|
||||
/*Nested=*/false);
|
||||
decl_context->addDecl(carbon_cpp_namespace);
|
||||
|
||||
// We provide custom lookup results within this namespace.
|
||||
carbon_cpp_namespace->setHasExternalVisibleStorage();
|
||||
}
|
||||
|
||||
// Register this file's package scope as corresponding to the `Carbon`
|
||||
// namespace in C++.
|
||||
@@ -610,34 +611,65 @@ static auto ParseTopLevelDecls(clang::Parser& parser,
|
||||
}
|
||||
}
|
||||
|
||||
// Injects the C++ code in `buffer` into the Clang preprocessor and parses it
|
||||
// as top-level declarations. Returns true on success, false if entering the
|
||||
// source file fails.
|
||||
static auto InjectAndParse(Context& context,
|
||||
std::unique_ptr<llvm::MemoryBuffer> buffer) -> bool {
|
||||
auto* cpp_context = context.cpp_context();
|
||||
CARBON_CHECK(cpp_context);
|
||||
|
||||
clang::Sema& sema = cpp_context->sema();
|
||||
clang::Preprocessor& preprocessor = sema.getPreprocessor();
|
||||
clang::Parser& parser = cpp_context->parser();
|
||||
|
||||
clang::FileID file_id =
|
||||
preprocessor.getSourceManager().createFileID(std::move(buffer));
|
||||
if (preprocessor.EnterSourceFile(file_id, nullptr, clang::SourceLocation())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parser.getCurToken().is(clang::tok::eof)) {
|
||||
parser.ConsumeToken();
|
||||
}
|
||||
ParseTopLevelDecls(parser, sema.getASTConsumer());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// An action and a set of registered Clang callbacks used to generate an AST
|
||||
// from a set of Cpp imports.
|
||||
class GenerateASTAction : public clang::ASTFrontendAction {
|
||||
public:
|
||||
explicit GenerateASTAction(
|
||||
Context& context,
|
||||
std::unique_ptr<CppDiagnosticListener> diagnostic_listener)
|
||||
: context_(&context),
|
||||
diagnostic_listener_(std::move(diagnostic_listener)) {}
|
||||
explicit GenerateASTAction(llvm::StringRef filename,
|
||||
llvm::LLVMContext* llvm_context)
|
||||
: filename_(filename), llvm_context_(llvm_context) {}
|
||||
|
||||
auto code_generator() const -> clang::CodeGenerator* {
|
||||
return code_generator_;
|
||||
}
|
||||
|
||||
auto TakeParser() -> std::unique_ptr<clang::Parser> {
|
||||
return std::move(parser_);
|
||||
}
|
||||
|
||||
protected:
|
||||
auto CreateASTConsumer(clang::CompilerInstance& clang_instance,
|
||||
llvm::StringRef /*file*/)
|
||||
-> std::unique_ptr<clang::ASTConsumer> override {
|
||||
auto& cpp_file = *context_->sem_ir().cpp_file();
|
||||
if (!cpp_file.llvm_context()) {
|
||||
if (!llvm_context_) {
|
||||
return std::make_unique<clang::ASTConsumer>();
|
||||
}
|
||||
auto code_generator =
|
||||
std::unique_ptr<clang::CodeGenerator>(clang::CreateLLVMCodeGen(
|
||||
cpp_file.diagnostics(), context_->sem_ir().filename(),
|
||||
clang_instance.getDiagnostics(), filename_,
|
||||
clang_instance.getVirtualFileSystemPtr(),
|
||||
clang_instance.getHeaderSearchOpts(),
|
||||
clang_instance.getPreprocessorOpts(),
|
||||
clang_instance.getCodeGenOpts(), *cpp_file.llvm_context()));
|
||||
cpp_file.SetCodeGenerator(code_generator.get());
|
||||
clang_instance.getCodeGenOpts(), *llvm_context_));
|
||||
code_generator_ = code_generator.get();
|
||||
return code_generator;
|
||||
}
|
||||
|
||||
@@ -657,18 +689,12 @@ class GenerateASTAction : public clang::ASTFrontendAction {
|
||||
clang_instance.createSema(getTranslationUnitKind(),
|
||||
/*CompletionConsumer=*/nullptr);
|
||||
|
||||
auto parser_ptr = std::make_unique<clang::Parser>(
|
||||
clang_instance.getPreprocessor(), clang_instance.getSema(),
|
||||
/*SkipFunctionBodies=*/false);
|
||||
auto& parser = *parser_ptr;
|
||||
parser_ = std::make_unique<clang::Parser>(clang_instance.getPreprocessor(),
|
||||
clang_instance.getSema(),
|
||||
/*SkipFunctionBodies=*/false);
|
||||
|
||||
clang_instance.getPreprocessor().EnterMainSourceFile();
|
||||
parser.Initialize();
|
||||
|
||||
auto cpp_context =
|
||||
std::make_unique<CppContext>(clang_instance, std::move(parser_ptr));
|
||||
cpp_context->set_diagnostic_listener(std::move(diagnostic_listener_));
|
||||
context_->set_cpp_context(std::move(cpp_context));
|
||||
parser_->Initialize();
|
||||
|
||||
if (auto* source = clang_instance.getASTContext().getExternalSource()) {
|
||||
source->StartTranslationUnit(&clang_instance.getASTConsumer());
|
||||
@@ -676,40 +702,47 @@ class GenerateASTAction : public clang::ASTFrontendAction {
|
||||
|
||||
clang_instance.getSema().ActOnStartOfTranslationUnit();
|
||||
|
||||
ParseTopLevelDecls(parser, clang_instance.getASTConsumer());
|
||||
ParseTopLevelDecls(*parser_, clang_instance.getASTConsumer());
|
||||
}
|
||||
|
||||
private:
|
||||
Context* context_;
|
||||
std::unique_ptr<CppDiagnosticListener> diagnostic_listener_;
|
||||
std::string filename_;
|
||||
llvm::LLVMContext* llvm_context_;
|
||||
clang::CodeGenerator* code_generator_ = nullptr;
|
||||
std::unique_ptr<clang::Parser> parser_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto GenerateAst(Context& context,
|
||||
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> base_invocation)
|
||||
-> bool {
|
||||
CARBON_CHECK(!context.cpp_context());
|
||||
CARBON_CHECK(!context.sem_ir().cpp_file());
|
||||
// Initializes the Clang state by building a new compiler invocation,
|
||||
// creating a diagnostics engine, and parsing a dummy main file containing a
|
||||
// semicolon. Returns the initialized state, or null on failure.
|
||||
auto InitializeCppDomain(
|
||||
Diagnostics::Consumer& consumer, llvm::StringRef filename,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> base_invocation)
|
||||
-> std::shared_ptr<CppDomain> {
|
||||
std::shared_ptr<clang::CompilerInstance> clang_instance;
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags;
|
||||
|
||||
// Build a new invocation.
|
||||
auto invocation =
|
||||
std::make_shared<ShallowCopyCompilerInvocation>(*base_invocation);
|
||||
|
||||
// Ask Clang to not leak memory.
|
||||
invocation->getFrontendOpts().DisableFree = false;
|
||||
|
||||
auto diagnostic_consumer =
|
||||
MakeDiagnosticConsumer(context.emitter().consumer(), invocation);
|
||||
auto* diagnostic_consumer_ptr = diagnostic_consumer.get();
|
||||
|
||||
// Build a diagnostics engine.
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
|
||||
clang::CompilerInstance::createDiagnostics(
|
||||
*fs, invocation->getDiagnosticOpts(), diagnostic_consumer.release(),
|
||||
/*ShouldOwnClient=*/true));
|
||||
diags = clang::CompilerInstance::createDiagnostics(
|
||||
*fs, invocation->getDiagnosticOpts(),
|
||||
MakeDiagnosticConsumer(consumer, invocation).release(),
|
||||
/*ShouldOwnClient=*/true);
|
||||
|
||||
// Ensure any diagnostics emitted in this function are flushed before we
|
||||
// return.
|
||||
auto on_exit =
|
||||
llvm::scope_exit([&]() { FlushDiagnosticConsumer(*diags->getClient()); });
|
||||
|
||||
// Extract the input from the frontend invocation and make sure it makes
|
||||
// sense.
|
||||
@@ -719,47 +752,31 @@ auto GenerateAst(Context& context,
|
||||
inputs[0].getKind().getFormat() == clang::InputKind::Source);
|
||||
llvm::StringRef file_name = inputs[0].getFile();
|
||||
|
||||
// Remap the imports file name to the corresponding `#include`s.
|
||||
// TODO: Modify the frontend options to specify this memory buffer as input
|
||||
// instead of remapping the file.
|
||||
std::string includes = GenerateCppIncludesHeaderCode(context, imports);
|
||||
auto includes_buffer =
|
||||
llvm::MemoryBuffer::getMemBufferCopy(includes, file_name);
|
||||
// Remap the input file to a dummy buffer containing a semicolon to start
|
||||
// with an empty AST. Clang requires at least one token in the main file
|
||||
// to avoid assertion failures if it later encounters module declarations.
|
||||
// TODO: See if we can fix this by injecting code into the main file rather
|
||||
// than entering nested buffers.
|
||||
auto empty_buffer = llvm::MemoryBuffer::getMemBuffer(";");
|
||||
invocation->getPreprocessorOpts().addRemappedFile(file_name,
|
||||
includes_buffer.release());
|
||||
empty_buffer.release());
|
||||
|
||||
auto clang_instance_ptr =
|
||||
std::make_unique<clang::CompilerInstance>(invocation);
|
||||
auto& clang_instance = *clang_instance_ptr;
|
||||
context.sem_ir().set_cpp_file(std::make_unique<SemIR::CppFile>(
|
||||
std::move(clang_instance_ptr), llvm_context));
|
||||
clang_instance = std::make_shared<clang::CompilerInstance>(invocation);
|
||||
|
||||
// Register an annotation scope to flush any Clang diagnostics when we return.
|
||||
// This ensures C++ diagnostics get flushed before `diags` is destroyed, and
|
||||
// that diagnostics created here don't interleave with later Carbon
|
||||
// diagnostics.
|
||||
Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
|
||||
[](auto& /*builder*/) {});
|
||||
|
||||
clang_instance.setDiagnostics(diags);
|
||||
clang_instance.setVirtualFileSystem(fs);
|
||||
clang_instance.createFileManager();
|
||||
clang_instance.createSourceManager();
|
||||
if (!clang_instance.createTarget()) {
|
||||
return false;
|
||||
clang_instance->setDiagnostics(diags);
|
||||
clang_instance->setVirtualFileSystem(fs);
|
||||
clang_instance->createFileManager();
|
||||
clang_instance->createSourceManager();
|
||||
if (!clang_instance->createTarget()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GenerateASTAction action(context, MakeContextDiagnosticListener(
|
||||
*diagnostic_consumer_ptr, context));
|
||||
if (!action.BeginSourceFile(clang_instance, inputs[0])) {
|
||||
return false;
|
||||
GenerateASTAction action(filename, llvm_context);
|
||||
if (!action.BeginSourceFile(*clang_instance, inputs[0])) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The AST context is now available, so the mangle context (used to compute
|
||||
// stable identities for imported C++ types) can be created.
|
||||
context.sem_ir().cpp_file()->CreateMangleContext();
|
||||
|
||||
auto& ast = clang_instance.getASTContext();
|
||||
auto& ast = clang_instance->getASTContext();
|
||||
|
||||
// Always build a multiplex source, even if there's only one child
|
||||
// source. During lowering, the `CarbonExternalASTSource` can no longer be
|
||||
@@ -777,18 +794,73 @@ auto GenerateAst(Context& context,
|
||||
ast.getExternalSource())) {
|
||||
multiplex_source->AddSource(existing_source);
|
||||
}
|
||||
multiplex_source->AddSource(
|
||||
llvm::makeIntrusiveRefCnt<CarbonExternalASTSource>(&context));
|
||||
ast.setExternalSource(std::move(multiplex_source_ref_cnt_ptr));
|
||||
|
||||
if (llvm::Error error = action.Execute()) {
|
||||
// `Execute` currently never fails, but its contract allows it to.
|
||||
context.TODO(SemIR::LocId::None, "failed to execute clang action: " +
|
||||
llvm::toString(std::move(error)));
|
||||
return false;
|
||||
CARBON_FATAL("Failed to execute clang action: {0}",
|
||||
llvm::toString(std::move(error)));
|
||||
}
|
||||
|
||||
return true;
|
||||
auto parser = action.TakeParser();
|
||||
CARBON_CHECK(parser);
|
||||
|
||||
return std::make_shared<CppDomain>(
|
||||
CppDomain{.clang_instance = std::move(clang_instance),
|
||||
.parser = std::move(parser),
|
||||
.code_generator = action.code_generator(),
|
||||
.llvm_context = llvm_context});
|
||||
}
|
||||
|
||||
auto GenerateAst(Context& context,
|
||||
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
|
||||
CppDomain& domain) -> bool {
|
||||
CARBON_CHECK(!context.cpp_context());
|
||||
CARBON_CHECK(!context.sem_ir().cpp_file());
|
||||
|
||||
// Register an annotation scope to flush any Clang diagnostics when we
|
||||
// return. This ensures C++ diagnostics get flushed before `diags` is
|
||||
// destroyed, and that diagnostics created here don't interleave with later
|
||||
// Carbon diagnostics.
|
||||
Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
|
||||
[](auto& /*builder*/) {});
|
||||
|
||||
auto clang_instance = domain.clang_instance;
|
||||
auto parser = domain.parser;
|
||||
|
||||
// Set up CppFile for the current SemIR::File.
|
||||
auto cpp_file =
|
||||
std::make_unique<SemIR::CppFile>(clang_instance, domain.llvm_context);
|
||||
if (domain.code_generator) {
|
||||
cpp_file->SetCodeGenerator(domain.code_generator);
|
||||
}
|
||||
context.sem_ir().set_cpp_file(std::move(cpp_file));
|
||||
|
||||
// Set up CppContext for the current Context.
|
||||
context.set_cpp_context(std::make_unique<CppContext>(
|
||||
*clang_instance, parser,
|
||||
MakeContextDiagnosticListener(
|
||||
*clang_instance->getDiagnostics().getClient(), context)));
|
||||
|
||||
// The AST context is now available, so the mangle context (used to compute
|
||||
// stable identities for imported C++ types) can be created.
|
||||
context.sem_ir().cpp_file()->CreateMangleContext();
|
||||
|
||||
// Add an external source referring to this context.
|
||||
auto* multiplex_source = cast<clang::MultiplexExternalSemaSource>(
|
||||
context.ast_context().getExternalSource());
|
||||
auto ast_source =
|
||||
llvm::makeIntrusiveRefCnt<CarbonExternalASTSource>(&context);
|
||||
multiplex_source->AddSource(ast_source);
|
||||
|
||||
// Map the package scope to the Carbon namespace.
|
||||
ast_source->BuildCarbonNamespace();
|
||||
|
||||
// Inject the imports-as-#includes buffer.
|
||||
std::string includes = GenerateCppIncludesHeaderCode(context, imports);
|
||||
auto buffer =
|
||||
llvm::MemoryBuffer::getMemBufferCopy(includes, "<shared cpp imports>");
|
||||
return InjectAndParse(context, std::move(buffer));
|
||||
}
|
||||
|
||||
auto InjectAstFromInlineCode(Context& context, SemIR::LocId loc_id,
|
||||
@@ -796,10 +868,6 @@ auto InjectAstFromInlineCode(Context& context, SemIR::LocId loc_id,
|
||||
auto* cpp_context = context.cpp_context();
|
||||
CARBON_CHECK(cpp_context);
|
||||
|
||||
clang::Sema& sema = cpp_context->sema();
|
||||
clang::Preprocessor& preprocessor = sema.getPreprocessor();
|
||||
clang::Parser& parser = cpp_context->parser();
|
||||
|
||||
RawStringOstream code_stream;
|
||||
AppendInlineCode(context, code_stream,
|
||||
context.parse_tree().node_token(loc_id.node_id()),
|
||||
@@ -807,22 +875,9 @@ auto InjectAstFromInlineCode(Context& context, SemIR::LocId loc_id,
|
||||
|
||||
auto buffer = llvm::MemoryBuffer::getMemBufferCopy(code_stream.TakeStr(),
|
||||
"<inline c++>");
|
||||
clang::FileID file_id =
|
||||
preprocessor.getSourceManager().createFileID(std::move(buffer));
|
||||
|
||||
if (preprocessor.EnterSourceFile(file_id, nullptr, clang::SourceLocation())) {
|
||||
// Clang will have generated a suitable error. There's nothing more to do
|
||||
// here.
|
||||
return;
|
||||
}
|
||||
|
||||
// The parser will typically have an EOF as its cached current token; consume
|
||||
// that so we can reach the newly-injected tokens.
|
||||
if (parser.getCurToken().is(clang::tok::eof)) {
|
||||
parser.ConsumeToken();
|
||||
}
|
||||
|
||||
ParseTopLevelDecls(parser, sema.getASTConsumer());
|
||||
// Clang will have generated a suitable error if this fails. There's nothing
|
||||
// more to do here.
|
||||
InjectAndParse(context, std::move(buffer));
|
||||
}
|
||||
|
||||
auto FinishAst(Context& context) -> void {
|
||||
@@ -830,7 +885,13 @@ auto FinishAst(Context& context) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
context.cpp_context()->sema().ActOnEndOfTranslationUnit();
|
||||
// Finalize the per-Context AST fragment. The final ActOnEndOfTranslationUnit
|
||||
// call for the CppDomain is performed in FinalizeCppDomain once all files
|
||||
// sharing the domain have been checked.
|
||||
context.cpp_context()->sema().ActOnEndOfTranslationUnitFragment(
|
||||
clang::TUFragmentKind::Normal);
|
||||
FlushDiagnosticConsumer(
|
||||
*context.cpp_context()->sema().getDiagnostics().getClient());
|
||||
context.emitter().Flush();
|
||||
|
||||
// Remove the `CarbonExternalASTSource` installed in `GenerateAst` and
|
||||
@@ -841,8 +902,7 @@ auto FinishAst(Context& context) -> void {
|
||||
auto* multiplex_source = cast<clang::MultiplexExternalSemaSource>(
|
||||
context.ast_context().getExternalSource());
|
||||
multiplex_source->EraseIf([](const auto& src) {
|
||||
// `CarbonExternalASTSource` inherits from `ReadOnlyASTSource`.
|
||||
return llvm::isa<SemIR::ReadOnlyASTSource>(src.get());
|
||||
return llvm::isa<CarbonExternalASTSource>(src.get());
|
||||
});
|
||||
multiplex_source->AddSource(
|
||||
llvm::makeIntrusiveRefCnt<SemIR::ReadOnlyASTSource>(context.sem_ir()));
|
||||
@@ -851,4 +911,12 @@ auto FinishAst(Context& context) -> void {
|
||||
context.set_cpp_context(nullptr);
|
||||
}
|
||||
|
||||
auto FinalizeCppDomain(CppDomain& domain) -> void {
|
||||
if (domain.clang_instance) {
|
||||
domain.clang_instance->getSema().ActOnEndOfTranslationUnit();
|
||||
FlushDiagnosticConsumer(
|
||||
*domain.clang_instance->getDiagnostics().getClient());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -17,19 +17,43 @@
|
||||
|
||||
namespace clang {
|
||||
class CompilerInvocation;
|
||||
class CompilerInstance;
|
||||
class Parser;
|
||||
class ExternalSemaSource;
|
||||
class CodeGenerator;
|
||||
} // namespace clang
|
||||
|
||||
namespace Carbon::Diagnostics {
|
||||
class Consumer;
|
||||
} // namespace Carbon::Diagnostics
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// A C++ compilation domain, including a live Clang instance that can be used to
|
||||
// parse more code into that domain. May be shared across multiple Carbon files.
|
||||
struct CppDomain {
|
||||
std::shared_ptr<clang::CompilerInstance> clang_instance;
|
||||
std::shared_ptr<clang::Parser> parser;
|
||||
clang::CodeGenerator* code_generator = nullptr;
|
||||
llvm::LLVMContext* llvm_context = nullptr;
|
||||
};
|
||||
|
||||
// Initializes a Clang compilation instance, which can be used to parse C++ code
|
||||
// within one or more Carbon files. Returns the initialized state, or null on
|
||||
// failure.
|
||||
auto InitializeCppDomain(
|
||||
Diagnostics::Consumer& consumer, llvm::StringRef filename,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> base_invocation)
|
||||
-> std::shared_ptr<CppDomain>;
|
||||
|
||||
// Generates a Clang AST for the given C++ imports and sets it as the context's
|
||||
// `cpp_context` and the SemIR's `cpp_file`. Returns a bool that represents
|
||||
// whether compilation was successful.
|
||||
auto GenerateAst(Context& context,
|
||||
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> base_invocation)
|
||||
-> bool;
|
||||
CppDomain& domain) -> bool;
|
||||
|
||||
// Injects C++ code from `inline Cpp` into the active Clang AST context.
|
||||
// Returns a bool representing whether parsing was successful.
|
||||
@@ -40,6 +64,9 @@ auto InjectAstFromInlineCode(Context& context, SemIR::LocId loc_id,
|
||||
// steps such as template instantiation and warning on unused declarations.
|
||||
auto FinishAst(Context& context) -> void;
|
||||
|
||||
// Finalizes a C++ domain at the end of checking all files.
|
||||
auto FinalizeCppDomain(CppDomain& domain) -> void;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_CHECK_CPP_GENERATE_AST_H_
|
||||
|
||||
@@ -118,9 +118,7 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
|
||||
|
||||
auto ImportCpp(Context& context,
|
||||
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> invocation) -> void {
|
||||
CppDomain* domain) -> void {
|
||||
if (imports.empty()) {
|
||||
// TODO: Consider always having a (non-null) AST even if there are no Cpp
|
||||
// imports.
|
||||
@@ -137,7 +135,7 @@ auto ImportCpp(Context& context,
|
||||
SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
|
||||
name_scope.set_is_closed_import(true);
|
||||
|
||||
if (GenerateAst(context, imports, fs, llvm_context, std::move(invocation))) {
|
||||
if (domain && GenerateAst(context, imports, *domain)) {
|
||||
name_scope.set_clang_decl_context_id(
|
||||
context.clang_decls().Add(
|
||||
{.key = SemIR::ClangDeclKey(
|
||||
|
||||
@@ -25,6 +25,8 @@ class VarDecl;
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
struct CppDomain;
|
||||
|
||||
// Returns whether the given function is an object member function. This is true
|
||||
// if it's a non-static member function and not a constructor. Object member
|
||||
// functions correspond to Carbon functions with a `self` parameter.
|
||||
@@ -33,12 +35,11 @@ auto IsObjectMemberFunction(const clang::FunctionDecl& decl) -> bool;
|
||||
|
||||
// Generates a C++ header that includes the imported cpp files, parses it,
|
||||
// generates the AST from it and links `SemIR::File` to it. Reports C++ errors
|
||||
// and warnings. If successful, adds a `Cpp` namespace.
|
||||
// and warnings. If successful, adds a `Cpp` namespace. `domain` should be
|
||||
// non-null unless there was an error initializing Clang.
|
||||
auto ImportCpp(Context& context,
|
||||
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> invocation) -> void;
|
||||
CppDomain* domain) -> void;
|
||||
|
||||
// Given a clang declaration ID that was previously imported into another file,
|
||||
// returns the corresponding clang declaration key in the current context.
|
||||
|
||||
@@ -21,7 +21,7 @@ import Cpp inline '''
|
||||
// CHECK:STDERR: 13 | export module Foo;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR: <carbon Cpp imports>:1:1: note: add 'module;' to the start of the file to introduce a global module fragment [CppInteropParseNote]
|
||||
// CHECK:STDERR: 1 | # 6 "fail_export_module_in_inline_cpp.carbon"
|
||||
// CHECK:STDERR: 1 | ;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR:
|
||||
export module Foo;
|
||||
@@ -37,7 +37,7 @@ import Cpp inline '''
|
||||
// CHECK:STDERR: 17 | module Foo;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR: <carbon Cpp imports>:1:1: note: add 'module;' to the start of the file to introduce a global module fragment [CppInteropParseNote]
|
||||
// CHECK:STDERR: 1 | # 6 "fail_module_in_inline_cpp.carbon"
|
||||
// CHECK:STDERR: 1 | ;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_module_in_inline_cpp.carbon:[[@LINE+4]]:8: error: module 'Foo' not found [CppInteropParseError]
|
||||
@@ -65,7 +65,7 @@ int n;
|
||||
// CHECK:STDERR: 25 | module Foo;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR: <carbon Cpp imports>:1:1: note: add 'module;' to the start of the file to introduce a global module fragment [CppInteropParseNote]
|
||||
// CHECK:STDERR: 1 | # 6 "fail_global_module_in_inline_cpp.carbon"
|
||||
// CHECK:STDERR: 1 | ;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_global_module_in_inline_cpp.carbon:[[@LINE+4]]:8: error: module 'Foo' not found [CppInteropParseError]
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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
|
||||
//
|
||||
// ARGS: compile --phase=check --share-cpp-ast %s
|
||||
//
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/basics/share_ast.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/basics/share_ast.carbon
|
||||
|
||||
// --- a.h
|
||||
#ifndef A_H
|
||||
#define A_H
|
||||
inline int GetA() { return 42; }
|
||||
#endif
|
||||
|
||||
// --- b.h
|
||||
#ifndef B_H
|
||||
#define B_H
|
||||
#include "a.h"
|
||||
inline int GetB() { return 100; }
|
||||
#endif
|
||||
|
||||
// --- a.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp library "a.h";
|
||||
|
||||
fn CallA() -> i32 {
|
||||
return Cpp.GetA();
|
||||
}
|
||||
|
||||
// --- todo_a_leaks.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp;
|
||||
|
||||
fn CallLeakedA() -> i32 {
|
||||
// TODO: This should not be found as we have not imported "a.h".
|
||||
return Cpp.GetA();
|
||||
}
|
||||
|
||||
// --- a_again.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp library "a.h";
|
||||
|
||||
fn CallAAgain() -> i32 {
|
||||
// This should be accepted as we have imported "a.h", even though we will just reuse the version from `a.carbon`.
|
||||
return Cpp.GetA();
|
||||
}
|
||||
|
||||
// --- b.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp library "b.h";
|
||||
|
||||
fn CallAIndirect() -> i32 {
|
||||
return Cpp.GetA();
|
||||
}
|
||||
|
||||
fn CallB() -> i32 {
|
||||
return Cpp.GetB();
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/export/thunk_ast.carbon
|
||||
// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc>
|
||||
// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <<carbon Cpp imports>:1:1> col:1
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> referenced Carbon external-linkage
|
||||
|
||||
// --- thunk_with_args_and_return.carbon
|
||||
|
||||
@@ -129,23 +129,22 @@ fn F() {
|
||||
|
||||
// --- static.h
|
||||
|
||||
// TODO: Promote this warning to an error by default.
|
||||
// CHECK:STDERR: ./static.h:[[@LINE+3]]:13: warning: function 'foo' has internal linkage but is not defined [CppInteropParseWarning]
|
||||
// CHECK:STDERR: 6 | static auto foo() -> void;
|
||||
// CHECK:STDERR: | ^
|
||||
static auto foo() -> void;
|
||||
|
||||
// --- todo_fail_import_static.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// TODO: Promote this warning to an error by default.
|
||||
// CHECK:STDERR: todo_fail_import_static.carbon:[[@LINE+4]]:10: in file included here [InCppInclude]
|
||||
// CHECK:STDERR: ./static.h:2:13: warning: function 'foo' has internal linkage but is not defined [CppInteropParseWarning]
|
||||
// CHECK:STDERR: 2 | static auto foo() -> void;
|
||||
// CHECK:STDERR: | ^
|
||||
import Cpp library "static.h";
|
||||
|
||||
fn F() {
|
||||
//@dump-sem-ir-begin
|
||||
// CHECK:STDERR: todo_fail_import_static.carbon:[[@LINE+4]]:11: note: used here [CppInteropParseNote]
|
||||
// CHECK:STDERR: 17 | Cpp.foo();
|
||||
// CHECK:STDERR: 12 | Cpp.foo();
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR:
|
||||
Cpp.foo();
|
||||
|
||||
@@ -36,23 +36,22 @@ fn MyF() {
|
||||
|
||||
// --- without_definition.h
|
||||
|
||||
// TODO: Promote this warning to an error by default.
|
||||
// CHECK:STDERR: ./without_definition.h:[[@LINE+3]]:13: warning: inline function 'foo' is not defined [CppInteropParseWarning]
|
||||
// CHECK:STDERR: 6 | inline void foo();
|
||||
// CHECK:STDERR: | ^
|
||||
inline void foo();
|
||||
|
||||
// --- todo_fail_import_without_definition.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// TODO: Promote this warning to an error by default.
|
||||
// CHECK:STDERR: todo_fail_import_without_definition.carbon:[[@LINE+4]]:10: in file included here [InCppInclude]
|
||||
// CHECK:STDERR: ./without_definition.h:2:13: warning: inline function 'foo' is not defined [CppInteropParseWarning]
|
||||
// CHECK:STDERR: 2 | inline void foo();
|
||||
// CHECK:STDERR: | ^
|
||||
import Cpp library "without_definition.h";
|
||||
|
||||
fn MyF() {
|
||||
//@dump-sem-ir-begin
|
||||
// CHECK:STDERR: todo_fail_import_without_definition.carbon:[[@LINE+4]]:11: note: used here [CppInteropParseNote]
|
||||
// CHECK:STDERR: 17 | Cpp.foo();
|
||||
// CHECK:STDERR: 12 | Cpp.foo();
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR:
|
||||
Cpp.foo();
|
||||
@@ -134,12 +133,12 @@ fn MyF() {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @MyF() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %foo.ref.loc17: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value]
|
||||
// CHECK:STDOUT: %foo.call.loc17: init %empty_tuple.type = call imports.%foo.decl()
|
||||
// CHECK:STDOUT: %Cpp.ref.loc20: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %foo.ref.loc20: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value]
|
||||
// CHECK:STDOUT: %foo.call.loc20: init %empty_tuple.type = call imports.%foo.decl()
|
||||
// CHECK:STDOUT: %Cpp.ref.loc12: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %foo.ref.loc12: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value]
|
||||
// CHECK:STDOUT: %foo.call.loc12: init %empty_tuple.type = call imports.%foo.decl()
|
||||
// CHECK:STDOUT: %Cpp.ref.loc15: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %foo.ref.loc15: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value]
|
||||
// CHECK:STDOUT: %foo.call.loc15: init %empty_tuple.type = call imports.%foo.decl()
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/import/thunk_ast.carbon
|
||||
// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc>
|
||||
// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <<carbon Cpp imports>:1:1> col:1
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Carbon external-linkage
|
||||
|
||||
// --- thunk_required.h
|
||||
@@ -33,6 +34,7 @@ auto foo(short a) -> void;
|
||||
// CHECK:STDOUT: |-InternalLinkageAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit
|
||||
// CHECK:STDOUT: `-AsmLabelAttr {{0x[a-f0-9]+}} <col:6> Implicit "_Z3foos.carbon_thunk._"
|
||||
// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc>
|
||||
// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <<carbon Cpp imports>:1:1> col:1
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Carbon external-linkage
|
||||
|
||||
// --- import_thunk_required.carbon
|
||||
|
||||
@@ -608,6 +608,7 @@ auto CompileDriver::Compile(DriverEnv& driver_env) -> DriverResult {
|
||||
options.vlog_stream = driver_env.vlog_stream;
|
||||
options.fuzzing = driver_env.fuzzing;
|
||||
options.mangle_string_fingerprint = options_->mangle_string_fingerprint;
|
||||
options.share_cpp_ast = options_->share_cpp_ast;
|
||||
if (options.vlog_stream || options_->dump_sem_ir || options_->dump_cpp_ast ||
|
||||
options_->dump_raw_sem_ir) {
|
||||
options.include_in_dumps = &cache_->include_in_dumps();
|
||||
|
||||
@@ -403,6 +403,17 @@ the `prelude_import` flag is set to false, this is also silently set to false.
|
||||
arg_b.Default(true);
|
||||
arg_b.Set(&include_carbon_core);
|
||||
});
|
||||
b.AddFlag(
|
||||
{
|
||||
.name = "share-cpp-ast",
|
||||
.help = R"""(
|
||||
Share a single Clang ASTContext across all compiled files.
|
||||
|
||||
TODO: This is a temporary measure and will be enabled by default and removed
|
||||
once this mode is fully implemented.
|
||||
)""",
|
||||
},
|
||||
[&](auto& arg_b) { arg_b.Set(&share_cpp_ast); });
|
||||
}
|
||||
|
||||
auto CompileOptions::BuildForBuildSubcommand(CommandLine::CommandBuilder& b,
|
||||
|
||||
@@ -122,6 +122,7 @@ struct CompileOptions {
|
||||
llvm::StringRef sem_ir_crash_dump;
|
||||
|
||||
bool mangle_string_fingerprint = false;
|
||||
bool share_cpp_ast = false;
|
||||
|
||||
// Get the LLVM optimization level corresponding to a Carbon optimization
|
||||
// level.
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ fn Call() -> i32 {
|
||||
// CHECK:STDOUT: !llvm.errno.tbaa = !{!9}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
|
||||
// CHECK:STDOUT: !1 = !DIFile(filename: "<carbon Cpp imports>", directory: "", checksumkind: CSK_MD5, checksum: "d41d8cd98f00b204e9800998ecf8427e")
|
||||
// CHECK:STDOUT: !1 = !DIFile(filename: "<carbon Cpp imports>", directory: "", checksumkind: CSK_MD5, checksum: "9eecb7db59d16c80417c72d1e1f4fbf1")
|
||||
// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
|
||||
// CHECK:STDOUT: !3 = !DIFile(filename: "debug_info.carbon", directory: "")
|
||||
// CHECK:STDOUT: !4 = !{i32 7, !"Dwarf Version", i32 5}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
CppFile::CppFile(std::unique_ptr<clang::CompilerInstance> clang,
|
||||
CppFile::CppFile(std::shared_ptr<clang::CompilerInstance> clang,
|
||||
llvm::LLVMContext* llvm_context)
|
||||
: clang_(std::move(clang)), llvm_context_(llvm_context) {}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Carbon::SemIR {
|
||||
// imported C++ headers and any inline C++ fragments.
|
||||
class CppFile {
|
||||
public:
|
||||
explicit CppFile(std::unique_ptr<clang::CompilerInstance> clang,
|
||||
explicit CppFile(std::shared_ptr<clang::CompilerInstance> clang,
|
||||
llvm::LLVMContext* llvm_context);
|
||||
~CppFile();
|
||||
|
||||
@@ -64,7 +64,7 @@ class CppFile {
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<clang::CompilerInstance> clang_;
|
||||
std::shared_ptr<clang::CompilerInstance> clang_;
|
||||
llvm::LLVMContext* llvm_context_;
|
||||
clang::CodeGenerator* code_generator_ = nullptr;
|
||||
// Created by `CreateMangleContext()` once the AST context is available.
|
||||
|
||||
Reference in New Issue
Block a user