Skip to content

Commit 613120e

Browse files
committed
Patch Out a Source of Iterator Invalidation
Synthesized file units were designed for autodiff to emit synthesized declarations, and also to sidestep the design implications of doing so late in the compiler pipeline. A call to materialize synthesized file units was added to the GetImplicitSendable request. This introduced a source of iterator invalidation into forEachFileToTypeCheck in whole-module builds. Any call to insert a new file into the module has the potential to cause the underlying SmallVector to reallocate. This patch provides a narrow workaround that stops using iterators altogether in forEachFileToTypeCheck. However, this bug reveals a severe architectural flaw in the concept of a synthesized file unit. Iterating over the files in a module is an extremely common operation, and there now are myriad ways we could wind up calling a function that mutates the module's list of files in the process. This also means the number and kind of files being visited by compiler analyses is dependent upon whether a request that inserts these files has or has not been called. This suggests the call to ModuleDecl::addFile in FileUnit::getOrCreateSynthesizedFile is deleterious and should be removed. Doing so will come as part of a larger refactoring. rdar://94043340
1 parent bbe08d7 commit 613120e

File tree

3 files changed

+22
-2
lines changed

3 files changed

+22
-2
lines changed

include/swift/AST/FileUnit.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
5555
/// Returns the synthesized file for this source file, if it exists.
5656
SynthesizedFileUnit *getSynthesizedFile() const;
5757

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

6066
/// Look up a (possibly overloaded) value set at top-level scope

lib/AST/Module.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3055,6 +3055,15 @@ SynthesizedFileUnit &FileUnit::getOrCreateSynthesizedFile() {
30553055
return *thisSynth;
30563056
SynthesizedFile = new (getASTContext()) SynthesizedFileUnit(*this);
30573057
SynthesizedFileAndKind.setPointer(SynthesizedFile);
3058+
// FIXME: Mutating the module in-flight is not a good idea. Any
3059+
// callers above us in the stack that are iterating over
3060+
// the module's files will have their iterators invalidated. There's
3061+
// a strong chance that whatever analysis led to this function being
3062+
// called is doing just that!
3063+
//
3064+
// Instead we ought to just call ModuleDecl::clearLookupCache() here
3065+
// and patch out the places looking for synthesized files hanging off of
3066+
// source files.
30583067
getParentModule()->addFile(*SynthesizedFile);
30593068
}
30603069
return *SynthesizedFile;

lib/Frontend/Frontend.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,8 +1166,13 @@ bool CompilerInstance::loadPartialModulesAndImplicitImports(
11661166
bool CompilerInstance::forEachFileToTypeCheck(
11671167
llvm::function_ref<bool(SourceFile &)> fn) {
11681168
if (isWholeModuleCompilation()) {
1169-
for (auto fileName : getMainModule()->getFiles()) {
1170-
auto *SF = dyn_cast<SourceFile>(fileName);
1169+
// FIXME: Do not refactor this to use an iterator as long as
1170+
// ModuleDecl::addFile is called during Sema. Synthesized files pushed
1171+
// during semantic analysis will cause iterator invalidation here.
1172+
// See notes in SourceFile::getOrCreateSynthesizedFile() for more.
1173+
unsigned i = 0;
1174+
while (i < getMainModule()->getFiles().size()) {
1175+
auto *SF = dyn_cast<SourceFile>(getMainModule()->getFiles()[i++]);
11711176
if (!SF) {
11721177
continue;
11731178
}

0 commit comments

Comments
 (0)