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:
Richard Smith
2026-07-28 00:19:40 +00:00
committed by GitHub
parent 4e77f9b6c8
commit 7d89ac98c7
25 changed files with 449 additions and 211 deletions
+44 -6
View File
@@ -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);
}
+5
View File
@@ -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
+2 -10
View File
@@ -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(
+4 -6
View File
@@ -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_;
+4 -7
View File
@@ -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());
+4 -8
View File
@@ -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.
+48 -17
View File
@@ -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;
};
+188 -120
View File
@@ -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
+31 -4
View File
@@ -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_
+2 -4
View File
@@ -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(
+5 -4
View File
@@ -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
+1
View File
@@ -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();
+11
View File
@@ -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,
+1
View File
@@ -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
View File
@@ -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}
+1 -1
View File
@@ -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) {}
+2 -2
View File
@@ -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.