Encapsulate clang::ASTUnit in SemIR::CppFile. (#6459)

This intends to avoid proliferation of dependencies on the exact API of
`clang::ASTUnit`, and would enable us to more easily switch to a
different approach that gives us more control over the construction of
the Clang AST.

Also remove some unnecessary tracking of the `CppFile` and instead
always retrieve it from the `SemIR::File`.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Richard Smith
2025-12-04 21:05:40 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent 78025fed70
commit d208e950c7
19 changed files with 187 additions and 124 deletions
+26 -28
View File
@@ -16,7 +16,6 @@
#include "clang/AST/UnresolvedSet.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/TextDiagnostic.h"
@@ -62,6 +61,7 @@
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/clang_decl.h"
#include "toolchain/sem_ir/class.h"
#include "toolchain/sem_ir/cpp_file.h"
#include "toolchain/sem_ir/cpp_overload_set.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/ids.h"
@@ -278,8 +278,9 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
// Outputs Carbon diagnostics based on the collected Clang diagnostics. Must
// be called after the AST is set in the context.
auto EmitDiagnostics() -> void {
CARBON_CHECK(sem_ir_->clang_ast_unit(),
"Attempted to emit diagnostics before the AST Unit is loaded");
CARBON_CHECK(
sem_ir_->cpp_file(),
"Attempted to emit C++ diagnostics before the C++ file is set");
for (size_t i = 0; i != diagnostic_infos_.size(); ++i) {
const ClangDiagnosticInfo& info = diagnostic_infos_[i];
@@ -388,15 +389,14 @@ class ShallowCopyCompilerInvocation : public clang::CompilerInvocation {
} // namespace
// Returns an AST for the C++ imports and a bool that represents whether
// compilation errors where encountered or the generated AST is null due to an
// error. Sets the AST in the context's `sem_ir`.
// TODO: Consider to always have a (non-null) AST.
// Generates a Clang AST for the C++ imports and sets it in the context's
// `sem_ir`. Returns a bool that represents whether compilation was successful.
// TODO: Consider to always have a (non-null) AST even if there are no Cpp
// imports.
static auto GenerateAst(
Context& context, llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
std::shared_ptr<clang::CompilerInvocation> base_invocation)
-> std::pair<std::unique_ptr<clang::ASTUnit>, bool> {
std::shared_ptr<clang::CompilerInvocation> base_invocation) -> bool {
auto invocation =
std::make_shared<ShallowCopyCompilerInvocation>(*base_invocation);
@@ -434,12 +434,13 @@ static auto GenerateAst(
// Attach the AST to SemIR. This needs to be done before we can emit any
// diagnostics, so their locations can be properly interpreted by our
// diagnostics machinery.
context.sem_ir().set_clang_ast_unit(ast.get());
context.sem_ir().set_cpp_file(
std::make_unique<SemIR::CppFile>(std::move(ast)));
// Emit any diagnostics we queued up while building the AST.
context.emitter().Flush();
return {std::move(ast), !ast || trap.hasErrorOccurred()};
return !trap.hasErrorOccurred();
}
// Adds a namespace for the `Cpp` import and returns its `NameScopeId`.
@@ -462,16 +463,15 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
.add_result.name_scope_id;
}
auto ImportCppFiles(Context& context,
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
std::shared_ptr<clang::CompilerInvocation> invocation)
-> std::unique_ptr<clang::ASTUnit> {
auto ImportCpp(Context& context,
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
std::shared_ptr<clang::CompilerInvocation> invocation) -> void {
if (imports.empty()) {
return nullptr;
return;
}
CARBON_CHECK(!context.sem_ir().clang_ast_unit());
CARBON_CHECK(!context.sem_ir().cpp_file());
PackageNameId package_id = imports.front().package_id;
CARBON_CHECK(
@@ -480,21 +480,19 @@ auto ImportCppFiles(Context& context,
}));
auto name_scope_id = AddNamespace(context, package_id, imports);
auto [generated_ast, ast_has_error] =
GenerateAst(context, imports, fs, std::move(invocation));
bool ast_has_error =
!GenerateAst(context, imports, fs, std::move(invocation));
SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
name_scope.set_is_closed_import(true);
name_scope.set_clang_decl_context_id(context.clang_decls().Add(
{.key = SemIR::ClangDeclKey(
generated_ast->getASTContext().getTranslationUnitDecl()),
{.key =
SemIR::ClangDeclKey(context.ast_context().getTranslationUnitDecl()),
.inst_id = name_scope.inst_id()}));
if (ast_has_error) {
name_scope.set_has_error();
}
return std::move(generated_ast);
}
// Returns the Clang `DeclContext` for the given name scope. Return the
@@ -2472,8 +2470,8 @@ auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
SemIR::ClassId class_id,
SemIR::ClangDeclId clang_decl_id)
-> bool {
clang::ASTUnit* ast = context.sem_ir().clang_ast_unit();
CARBON_CHECK(ast);
SemIR::CppFile* cpp_file = context.sem_ir().cpp_file();
CARBON_CHECK(cpp_file);
auto* clang_decl =
cast<clang::TagDecl>(context.clang_decls().Get(clang_decl_id).key.decl);
@@ -2494,8 +2492,8 @@ auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
// Ask Clang whether the type is complete. This triggers template
// instantiation if necessary.
clang::DiagnosticErrorTrap trap(ast->getDiagnostics());
if (!ast->getSema().isCompleteType(
clang::DiagnosticErrorTrap trap(cpp_file->diagnostics());
if (!cpp_file->sema().isCompleteType(
loc, context.ast_context().getCanonicalTagType(clang_decl))) {
// Type is incomplete. Nothing more to do, but tell the caller if we
// produced an error.