Skip to content

Commit f5fdd43

Browse files
authored
[clang] Refactor ASTUnit::LoadFromASTFile() (#164265)
This PR refactors `ASTUnit::LoadFromASTFile()` to be easier to follow. Conceptually, it tries to read an AST file, adopt the serialized options, and set up `Sema` and `ASTContext` to deserialize the AST file contents on-demand. The implementation of this used to be spread across an `ASTReaderListener` and the function in question. Figuring out what listener method gets called when and how it's supposed to interact with the rest of the functionality was very unclear. The `FileManager`'s VFS was being swapped-out during deserialization, the options were being adopted by `Preprocessor` and others just-in-time to pass `ASTReader`'s validation checks, and the target was being initialized somewhere in between all of this. This lead to a very muddy semantics. This PR splits `ASTUnit::LoadFromASTFile()` into three distinct steps: 1. Read out the options from the AST file. 2. Initialize objects from the VFS to the `ASTContext`. 3. Load the AST file and hook it up with the compiler objects. This should be much easier to understand, and I've done my best to clearly document the remaining gotchas. (This was originally motivated by the desire to remove `FileManager::setVirtualFileSystem()` and make it impossible to swap out VFSs from underneath `FileManager` mid-compile.)
1 parent a7672fe commit f5fdd43

File tree

2 files changed

+144
-143
lines changed

2 files changed

+144
-143
lines changed

clang/lib/Frontend/ASTUnit.cpp

Lines changed: 137 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -512,152 +512,73 @@ namespace {
512512
/// Gathers information from ASTReader that will be used to initialize
513513
/// a Preprocessor.
514514
class ASTInfoCollector : public ASTReaderListener {
515-
Preprocessor &PP;
516-
ASTContext *Context;
517515
HeaderSearchOptions &HSOpts;
516+
std::string &SpecificModuleCachePath;
518517
PreprocessorOptions &PPOpts;
519-
LangOptions &LangOpt;
518+
LangOptions &LangOpts;
520519
CodeGenOptions &CodeGenOpts;
521-
std::shared_ptr<TargetOptions> &TargetOpts;
522-
IntrusiveRefCntPtr<TargetInfo> &Target;
520+
TargetOptions &TargetOpts;
523521
unsigned &Counter;
524-
bool InitializedLanguage = false;
525-
bool InitializedHeaderSearchPaths = false;
526522

527523
public:
528-
ASTInfoCollector(Preprocessor &PP, ASTContext *Context,
529-
HeaderSearchOptions &HSOpts, PreprocessorOptions &PPOpts,
530-
LangOptions &LangOpt, CodeGenOptions &CodeGenOpts,
531-
std::shared_ptr<TargetOptions> &TargetOpts,
532-
IntrusiveRefCntPtr<TargetInfo> &Target, unsigned &Counter)
533-
: PP(PP), Context(Context), HSOpts(HSOpts), PPOpts(PPOpts),
534-
LangOpt(LangOpt), CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts),
535-
Target(Target), Counter(Counter) {}
536-
537-
bool ReadLanguageOptions(const LangOptions &LangOpts,
524+
ASTInfoCollector(HeaderSearchOptions &HSOpts,
525+
std::string &SpecificModuleCachePath,
526+
PreprocessorOptions &PPOpts, LangOptions &LangOpts,
527+
CodeGenOptions &CodeGenOpts, TargetOptions &TargetOpts,
528+
unsigned &Counter)
529+
: HSOpts(HSOpts), SpecificModuleCachePath(SpecificModuleCachePath),
530+
PPOpts(PPOpts), LangOpts(LangOpts), CodeGenOpts(CodeGenOpts),
531+
TargetOpts(TargetOpts), Counter(Counter) {}
532+
533+
bool ReadLanguageOptions(const LangOptions &NewLangOpts,
538534
StringRef ModuleFilename, bool Complain,
539535
bool AllowCompatibleDifferences) override {
540-
if (InitializedLanguage)
541-
return false;
542-
543-
// FIXME: We did similar things in ReadHeaderSearchOptions too. But such
544-
// style is not scaling. Probably we need to invite some mechanism to
545-
// handle such patterns generally.
546-
auto PICLevel = LangOpt.PICLevel;
547-
auto PIE = LangOpt.PIE;
548-
549-
LangOpt = LangOpts;
550-
551-
LangOpt.PICLevel = PICLevel;
552-
LangOpt.PIE = PIE;
553-
554-
InitializedLanguage = true;
555-
556-
updated();
536+
LangOpts = NewLangOpts;
557537
return false;
558538
}
559539

560-
bool ReadCodeGenOptions(const CodeGenOptions &CGOpts,
540+
bool ReadCodeGenOptions(const CodeGenOptions &NewCodeGenOpts,
561541
StringRef ModuleFilename, bool Complain,
562542
bool AllowCompatibleDifferences) override {
563-
this->CodeGenOpts = CGOpts;
543+
CodeGenOpts = NewCodeGenOpts;
564544
return false;
565545
}
566546

567-
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
547+
bool ReadHeaderSearchOptions(const HeaderSearchOptions &NewHSOpts,
568548
StringRef ModuleFilename,
569-
StringRef SpecificModuleCachePath,
549+
StringRef NewSpecificModuleCachePath,
570550
bool Complain) override {
571-
// llvm::SaveAndRestore doesn't support bit field.
572-
auto ForceCheckCXX20ModulesInputFiles =
573-
this->HSOpts.ForceCheckCXX20ModulesInputFiles;
574-
llvm::SaveAndRestore X(this->HSOpts.UserEntries);
575-
llvm::SaveAndRestore Y(this->HSOpts.SystemHeaderPrefixes);
576-
llvm::SaveAndRestore Z(this->HSOpts.VFSOverlayFiles);
577-
578-
this->HSOpts = HSOpts;
579-
this->HSOpts.ForceCheckCXX20ModulesInputFiles =
580-
ForceCheckCXX20ModulesInputFiles;
581-
551+
HSOpts = NewHSOpts;
552+
SpecificModuleCachePath = NewSpecificModuleCachePath;
582553
return false;
583554
}
584555

585-
bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
556+
bool ReadHeaderSearchPaths(const HeaderSearchOptions &NewHSOpts,
586557
bool Complain) override {
587-
if (InitializedHeaderSearchPaths)
588-
return false;
589-
590-
this->HSOpts.UserEntries = HSOpts.UserEntries;
591-
this->HSOpts.SystemHeaderPrefixes = HSOpts.SystemHeaderPrefixes;
592-
this->HSOpts.VFSOverlayFiles = HSOpts.VFSOverlayFiles;
593-
594-
// Initialize the FileManager. We can't do this in update(), since that
595-
// performs the initialization too late (once both target and language
596-
// options are read).
597-
PP.getFileManager().setVirtualFileSystem(createVFSFromOverlayFiles(
598-
HSOpts.VFSOverlayFiles, PP.getDiagnostics(),
599-
PP.getFileManager().getVirtualFileSystemPtr()));
600-
601-
InitializedHeaderSearchPaths = true;
602-
558+
HSOpts.UserEntries = NewHSOpts.UserEntries;
559+
HSOpts.SystemHeaderPrefixes = NewHSOpts.SystemHeaderPrefixes;
560+
HSOpts.VFSOverlayFiles = NewHSOpts.VFSOverlayFiles;
603561
return false;
604562
}
605563

606-
bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
564+
bool ReadPreprocessorOptions(const PreprocessorOptions &NewPPOpts,
607565
StringRef ModuleFilename, bool ReadMacros,
608566
bool Complain,
609567
std::string &SuggestedPredefines) override {
610-
this->PPOpts = PPOpts;
568+
PPOpts = NewPPOpts;
611569
return false;
612570
}
613571

614-
bool ReadTargetOptions(const TargetOptions &TargetOpts,
572+
bool ReadTargetOptions(const TargetOptions &NewTargetOpts,
615573
StringRef ModuleFilename, bool Complain,
616574
bool AllowCompatibleDifferences) override {
617-
// If we've already initialized the target, don't do it again.
618-
if (Target)
619-
return false;
620-
621-
this->TargetOpts = std::make_shared<TargetOptions>(TargetOpts);
622-
Target =
623-
TargetInfo::CreateTargetInfo(PP.getDiagnostics(), *this->TargetOpts);
624-
625-
updated();
575+
TargetOpts = NewTargetOpts;
626576
return false;
627577
}
628578

629579
void ReadCounter(const serialization::ModuleFile &M,
630-
unsigned Value) override {
631-
Counter = Value;
632-
}
633-
634-
private:
635-
void updated() {
636-
if (!Target || !InitializedLanguage)
637-
return;
638-
639-
// Inform the target of the language options.
640-
//
641-
// FIXME: We shouldn't need to do this, the target should be immutable once
642-
// created. This complexity should be lifted elsewhere.
643-
Target->adjust(PP.getDiagnostics(), LangOpt, /*AuxTarget=*/nullptr);
644-
645-
// Initialize the preprocessor.
646-
PP.Initialize(*Target);
647-
648-
if (!Context)
649-
return;
650-
651-
// Initialize the ASTContext
652-
Context->InitBuiltinTypes(*Target);
653-
654-
// Adjust printing policy based on language options.
655-
Context->setPrintingPolicy(PrintingPolicy(LangOpt));
656-
657-
// We didn't have access to the comment options when the ASTContext was
658-
// constructed, so register them now.
659-
Context->getCommentCommandTraits().registerCommentOptions(
660-
LangOpt.CommentOpts);
580+
unsigned NewCounter) override {
581+
Counter = NewCounter;
661582
}
662583
};
663584

@@ -812,7 +733,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
812733
std::shared_ptr<DiagnosticOptions> DiagOpts,
813734
IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
814735
const FileSystemOptions &FileSystemOpts, const HeaderSearchOptions &HSOpts,
815-
const LangOptions *LangOpts, bool OnlyLocalDecls,
736+
const LangOptions *ProvidedLangOpts, bool OnlyLocalDecls,
816737
CaptureDiagsKind CaptureDiagnostics, bool AllowASTWithCompilerErrors,
817738
bool UserFilesAreVolatile) {
818739
std::unique_ptr<ASTUnit> AST(new ASTUnit(true));
@@ -826,66 +747,132 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
826747

827748
ConfigureDiags(Diags, *AST, CaptureDiagnostics);
828749

829-
AST->LangOpts = LangOpts ? std::make_unique<LangOptions>(*LangOpts)
830-
: std::make_unique<LangOptions>();
750+
std::unique_ptr<LangOptions> LocalLangOpts;
751+
const LangOptions &LangOpts = [&]() -> const LangOptions & {
752+
if (ProvidedLangOpts)
753+
return *ProvidedLangOpts;
754+
LocalLangOpts = std::make_unique<LangOptions>();
755+
return *LocalLangOpts;
756+
}();
757+
758+
AST->LangOpts = std::make_unique<LangOptions>(LangOpts);
831759
AST->OnlyLocalDecls = OnlyLocalDecls;
832760
AST->CaptureDiagnostics = CaptureDiagnostics;
833761
AST->DiagOpts = DiagOpts;
834762
AST->Diagnostics = Diags;
835-
AST->FileMgr = llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOpts, VFS);
836763
AST->UserFilesAreVolatile = UserFilesAreVolatile;
837-
AST->SourceMgr = llvm::makeIntrusiveRefCnt<SourceManager>(
838-
AST->getDiagnostics(), AST->getFileManager(), UserFilesAreVolatile);
839-
AST->ModCache = createCrossProcessModuleCache();
840764
AST->HSOpts = std::make_unique<HeaderSearchOptions>(HSOpts);
841765
AST->HSOpts->ModuleFormat = std::string(PCHContainerRdr.getFormats().front());
842-
AST->HeaderInfo.reset(new HeaderSearch(AST->getHeaderSearchOpts(),
843-
AST->getSourceManager(),
844-
AST->getDiagnostics(),
845-
AST->getLangOpts(),
846-
/*Target=*/nullptr));
847766
AST->PPOpts = std::make_shared<PreprocessorOptions>();
767+
AST->CodeGenOpts = std::make_unique<CodeGenOptions>();
768+
AST->TargetOpts = std::make_shared<TargetOptions>();
769+
770+
AST->ModCache = createCrossProcessModuleCache();
771+
772+
// Gather info for preprocessor construction later on.
773+
std::string SpecificModuleCachePath;
774+
unsigned Counter = 0;
775+
// Using a temporary FileManager since the AST file might specify custom
776+
// HeaderSearchOptions::VFSOverlayFiles that affect the underlying VFS.
777+
FileManager TmpFileMgr(FileSystemOpts, VFS);
778+
ASTInfoCollector Collector(*AST->HSOpts, SpecificModuleCachePath,
779+
*AST->PPOpts, *AST->LangOpts, *AST->CodeGenOpts,
780+
*AST->TargetOpts, Counter);
781+
if (ASTReader::readASTFileControlBlock(
782+
Filename, TmpFileMgr, *AST->ModCache, PCHContainerRdr,
783+
/*FindModuleFileExtensions=*/true, Collector,
784+
/*ValidateDiagnosticOptions=*/true, ASTReader::ARR_None)) {
785+
AST->getDiagnostics().Report(diag::err_fe_unable_to_load_pch);
786+
return nullptr;
787+
}
788+
789+
VFS = createVFSFromOverlayFiles(AST->HSOpts->VFSOverlayFiles,
790+
*AST->Diagnostics, std::move(VFS));
848791

849-
// Gather Info for preprocessor construction later on.
792+
AST->FileMgr = llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOpts, VFS);
793+
794+
AST->SourceMgr = llvm::makeIntrusiveRefCnt<SourceManager>(
795+
AST->getDiagnostics(), AST->getFileManager(), UserFilesAreVolatile);
850796

851-
HeaderSearch &HeaderInfo = *AST->HeaderInfo;
797+
AST->HSOpts->PrebuiltModuleFiles = HSOpts.PrebuiltModuleFiles;
798+
AST->HSOpts->PrebuiltModulePaths = HSOpts.PrebuiltModulePaths;
799+
AST->HeaderInfo = std::make_unique<HeaderSearch>(
800+
AST->getHeaderSearchOpts(), AST->getSourceManager(),
801+
AST->getDiagnostics(), AST->getLangOpts(),
802+
/*Target=*/nullptr);
803+
AST->HeaderInfo->setModuleCachePath(SpecificModuleCachePath);
852804

853805
AST->PP = std::make_shared<Preprocessor>(
854806
*AST->PPOpts, AST->getDiagnostics(), *AST->LangOpts,
855-
AST->getSourceManager(), HeaderInfo, AST->ModuleLoader,
807+
AST->getSourceManager(), *AST->HeaderInfo, AST->ModuleLoader,
856808
/*IILookup=*/nullptr,
857809
/*OwnsHeaderSearch=*/false);
858-
Preprocessor &PP = *AST->PP;
859810

860811
if (ToLoad >= LoadASTOnly)
861812
AST->Ctx = llvm::makeIntrusiveRefCnt<ASTContext>(
862-
*AST->LangOpts, AST->getSourceManager(), PP.getIdentifierTable(),
863-
PP.getSelectorTable(), PP.getBuiltinInfo(),
813+
*AST->LangOpts, AST->getSourceManager(), AST->PP->getIdentifierTable(),
814+
AST->PP->getSelectorTable(), AST->PP->getBuiltinInfo(),
864815
AST->getTranslationUnitKind());
865816

866817
DisableValidationForModuleKind disableValid =
867818
DisableValidationForModuleKind::None;
868819
if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION"))
869820
disableValid = DisableValidationForModuleKind::All;
870821
AST->Reader = llvm::makeIntrusiveRefCnt<ASTReader>(
871-
PP, *AST->ModCache, AST->Ctx.get(), PCHContainerRdr, *AST->CodeGenOpts,
872-
ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
822+
*AST->PP, *AST->ModCache, AST->Ctx.get(), PCHContainerRdr,
823+
*AST->CodeGenOpts, ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
873824
/*isysroot=*/"",
874825
/*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors);
875826

876-
unsigned Counter = 0;
877-
AST->Reader->setListener(std::make_unique<ASTInfoCollector>(
878-
*AST->PP, AST->Ctx.get(), *AST->HSOpts, *AST->PPOpts, *AST->LangOpts,
879-
*AST->CodeGenOpts, AST->TargetOpts, AST->Target, Counter));
880-
881-
// Attach the AST reader to the AST context as an external AST
882-
// source, so that declarations will be deserialized from the
883-
// AST file as needed.
827+
// Attach the AST reader to the AST context as an external AST source, so that
828+
// declarations will be deserialized from the AST file as needed.
884829
// We need the external source to be set up before we read the AST, because
885830
// eagerly-deserialized declarations may use it.
886831
if (AST->Ctx)
887832
AST->Ctx->setExternalSource(AST->Reader);
888833

834+
AST->Target =
835+
TargetInfo::CreateTargetInfo(AST->PP->getDiagnostics(), *AST->TargetOpts);
836+
// Inform the target of the language options.
837+
//
838+
// FIXME: We shouldn't need to do this, the target should be immutable once
839+
// created. This complexity should be lifted elsewhere.
840+
AST->Target->adjust(AST->PP->getDiagnostics(), *AST->LangOpts,
841+
/*AuxTarget=*/nullptr);
842+
843+
// Initialize the preprocessor.
844+
AST->PP->Initialize(*AST->Target);
845+
846+
AST->PP->setCounterValue(Counter);
847+
848+
if (AST->Ctx) {
849+
// Initialize the ASTContext
850+
AST->Ctx->InitBuiltinTypes(*AST->Target);
851+
852+
// Adjust printing policy based on language options.
853+
AST->Ctx->setPrintingPolicy(PrintingPolicy(*AST->LangOpts));
854+
855+
// We didn't have access to the comment options when the ASTContext was
856+
// constructed, so register them now.
857+
AST->Ctx->getCommentCommandTraits().registerCommentOptions(
858+
AST->LangOpts->CommentOpts);
859+
}
860+
861+
// The temporary FileManager we used for ASTReader::readASTFileControlBlock()
862+
// might have already read stdin, and reading it again will fail. Let's
863+
// explicitly forward the buffer.
864+
if (Filename == "-")
865+
if (auto FE = llvm::expectedToOptional(TmpFileMgr.getSTDIN()))
866+
if (auto BufRef = TmpFileMgr.getBufferForFile(*FE)) {
867+
auto Buf = llvm::MemoryBuffer::getMemBufferCopy(
868+
(*BufRef)->getBuffer(), (*BufRef)->getBufferIdentifier());
869+
AST->Reader->getModuleManager().addInMemoryBuffer("-", std::move(Buf));
870+
}
871+
872+
// Reinstate the provided options that are relevant for reading AST files.
873+
AST->HSOpts->ForceCheckCXX20ModulesInputFiles =
874+
HSOpts.ForceCheckCXX20ModulesInputFiles;
875+
889876
switch (AST->Reader->ReadAST(Filename, serialization::MK_MainFile,
890877
SourceLocation(), ASTReader::ARR_None)) {
891878
case ASTReader::Success:
@@ -901,11 +888,18 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
901888
return nullptr;
902889
}
903890

904-
AST->OriginalSourceFile = std::string(AST->Reader->getOriginalSourceFile());
891+
// Now that we have successfully loaded the AST file, we can reinstate some
892+
// options that the clients expect us to preserve (but would trip AST file
893+
// validation, so we couldn't set them earlier).
894+
AST->HSOpts->UserEntries = HSOpts.UserEntries;
895+
AST->HSOpts->SystemHeaderPrefixes = HSOpts.SystemHeaderPrefixes;
896+
AST->HSOpts->VFSOverlayFiles = HSOpts.VFSOverlayFiles;
897+
AST->LangOpts->PICLevel = LangOpts.PICLevel;
898+
AST->LangOpts->PIE = LangOpts.PIE;
905899

906-
PP.setCounterValue(Counter);
900+
AST->OriginalSourceFile = std::string(AST->Reader->getOriginalSourceFile());
907901

908-
Module *M = HeaderInfo.lookupModule(AST->getLangOpts().CurrentModule);
902+
Module *M = AST->HeaderInfo->lookupModule(AST->getLangOpts().CurrentModule);
909903
if (M && AST->getLangOpts().isCompilingModule() && M->isNamedModule())
910904
AST->Ctx->setCurrentNamedModule(M);
911905

@@ -915,13 +909,14 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
915909

916910
// Create a semantic analysis object and tell the AST reader about it.
917911
if (ToLoad >= LoadEverything) {
918-
AST->TheSema.reset(new Sema(PP, *AST->Ctx, *AST->Consumer));
912+
AST->TheSema = std::make_unique<Sema>(*AST->PP, *AST->Ctx, *AST->Consumer);
919913
AST->TheSema->Initialize();
920914
AST->Reader->InitializeSema(*AST->TheSema);
921915
}
922916

923917
// Tell the diagnostic client that we have started a source file.
924-
AST->getDiagnostics().getClient()->BeginSourceFile(PP.getLangOpts(), &PP);
918+
AST->getDiagnostics().getClient()->BeginSourceFile(AST->PP->getLangOpts(),
919+
AST->PP.get());
925920

926921
return AST;
927922
}

0 commit comments

Comments
 (0)