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
+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> {