Skip to content

Don't Mutate ModuleDecl's File List When Creating Synthesized Files #59383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions include/swift/AST/FileUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {

/// Returns the synthesized file for this source file, creating one and
/// inserting it into the module if it does not exist.
///
/// \warning Because this function mutates the parent module's list of files,
/// it will invalidate the iterators of any upstream callers of
/// \c ModuleDecl::getFiles().
SynthesizedFileUnit &getOrCreateSynthesizedFile();

/// Look up a (possibly overloaded) value set at top-level scope
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,15 @@ class ModuleDecl
return { Files.begin(), Files.size() };
}

/// Add the given file to this module.
///
/// FIXME: Remove this function from public view. Modules never need to add
/// files once they are created.
///
/// \warning There are very few safe points to call this function once a
/// \c ModuleDecl has been created. If you find yourself needing to insert
/// a file in the middle of e.g. semantic analysis, use a \c
/// SynthesizedFileUnit instead.
void addFile(FileUnit &newFile);

/// Creates a map from \c #filePath strings to corresponding \c #fileID
Expand Down
31 changes: 14 additions & 17 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,13 @@ SourceLookupCache::SourceLookupCache(const ModuleDecl &M) {
FrontendStatsTracer tracer(M.getASTContext().Stats,
"module-populate-cache");
for (const FileUnit *file : M.getFiles()) {
if (auto *SFU = dyn_cast<SynthesizedFileUnit>(file)) {
addToUnqualifiedLookupCache(SFU->getTopLevelDecls(), false);
continue;
}
auto *SF = cast<SourceFile>(file);
addToUnqualifiedLookupCache(SF->getTopLevelDecls(), false);
addToUnqualifiedLookupCache(SF->getHoistedDecls(), false);

if (auto *SFU = file->getSynthesizedFile()) {
addToUnqualifiedLookupCache(SFU->getTopLevelDecls(), false);
}
}
}

Expand Down Expand Up @@ -548,9 +548,13 @@ SourceFile *CodeCompletionFileRequest::evaluate(Evaluator &evaluator,
llvm_unreachable("Couldn't find the completion file?");
}

#define FORWARD(name, args) \
for (const FileUnit *file : getFiles()) \
file->name args;
#define FORWARD(name, args) \
for (const FileUnit *file : getFiles()) { \
file->name args; \
if (auto *synth = file->getSynthesizedFile()) { \
synth->name args; \
} \
}

SourceLookupCache &ModuleDecl::getSourceLookupCache() const {
if (!Cache) {
Expand Down Expand Up @@ -3064,16 +3068,9 @@ SynthesizedFileUnit &FileUnit::getOrCreateSynthesizedFile() {
return *thisSynth;
SynthesizedFile = new (getASTContext()) SynthesizedFileUnit(*this);
SynthesizedFileAndKind.setPointer(SynthesizedFile);
// FIXME: Mutating the module in-flight is not a good idea. Any
// callers above us in the stack that are iterating over
// the module's files will have their iterators invalidated. There's
// a strong chance that whatever analysis led to this function being
// called is doing just that!
//
// Instead we ought to just call ModuleDecl::clearLookupCache() here
// and patch out the places looking for synthesized files hanging off of
// source files.
getParentModule()->addFile(*SynthesizedFile);
// Rebuild the source lookup caches now that we have a synthesized file
// full of declarations to look into.
getParentModule()->clearLookupCache();
}
return *SynthesizedFile;
}
Expand Down
10 changes: 2 additions & 8 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,19 +1168,13 @@ bool CompilerInstance::loadPartialModulesAndImplicitImports(
bool CompilerInstance::forEachFileToTypeCheck(
llvm::function_ref<bool(SourceFile &)> fn) {
if (isWholeModuleCompilation()) {
// FIXME: Do not refactor this to use an iterator as long as
// ModuleDecl::addFile is called during Sema. Synthesized files pushed
// during semantic analysis will cause iterator invalidation here.
// See notes in SourceFile::getOrCreateSynthesizedFile() for more.
unsigned i = 0;
while (i < getMainModule()->getFiles().size()) {
auto *SF = dyn_cast<SourceFile>(getMainModule()->getFiles()[i++]);
for (auto fileName : getMainModule()->getFiles()) {
auto *SF = dyn_cast<SourceFile>(fileName);
if (!SF) {
continue;
}
if (fn(*SF))
return true;
;
}
} else {
for (auto *SF : getPrimarySourceFiles()) {
Expand Down
19 changes: 12 additions & 7 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1096,8 +1096,9 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator,
for (auto *file : filesToEmit) {
if (auto *nextSF = dyn_cast<SourceFile>(file)) {
IGM.emitSourceFile(*nextSF);
} else if (auto *nextSFU = dyn_cast<SynthesizedFileUnit>(file)) {
IGM.emitSynthesizedFileUnit(*nextSFU);
if (auto *synthSFU = file->getSynthesizedFile()) {
IGM.emitSynthesizedFileUnit(*synthSFU);
}
} else {
file->collectLinkLibraries([&IGM](LinkLibrary LinkLib) {
IGM.addLinkLibrary(LinkLib);
Expand Down Expand Up @@ -1339,11 +1340,15 @@ static void performParallelIRGeneration(IRGenDescriptor desc) {

for (auto *File : M->getFiles()) {
if (auto *SF = dyn_cast<SourceFile>(File)) {
CurrentIGMPtr IGM = irgen.getGenModule(SF);
IGM->emitSourceFile(*SF);
} else if (auto *nextSFU = dyn_cast<SynthesizedFileUnit>(File)) {
CurrentIGMPtr IGM = irgen.getGenModule(nextSFU);
IGM->emitSynthesizedFileUnit(*nextSFU);
{
CurrentIGMPtr IGM = irgen.getGenModule(SF);
IGM->emitSourceFile(*SF);
}

if (auto *synthSFU = File->getSynthesizedFile()) {
CurrentIGMPtr IGM = irgen.getGenModule(synthSFU);
IGM->emitSynthesizedFileUnit(*synthSFU);
}
} else {
File->collectLinkLibraries([&](LinkLibrary LinkLib) {
irgen.getPrimaryIGM()->addLinkLibrary(LinkLib);
Expand Down
3 changes: 0 additions & 3 deletions lib/IRGen/IRGenRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ TinyPtrVector<FileUnit *> IRGenDescriptor::getFilesToEmit() const {
TinyPtrVector<FileUnit *> files;
files.push_back(primary);

if (auto *synthesizedFile = primary->getSynthesizedFile())
files.push_back(synthesizedFile);

return files;
}

Expand Down
4 changes: 4 additions & 0 deletions lib/TBDGen/TBDGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,10 @@ void TBDGenVisitor::visit(const TBDGenDescriptor &desc) {
llvm::for_each(Modules, [&](ModuleDecl *M) {
for (auto *file : M->getFiles()) {
visitFile(file);

// Visit synthesized file, if it exists.
if (auto *synthesizedFile = file->getSynthesizedFile())
visitFile(synthesizedFile);
}
});
}
Expand Down