Skip to content

Commit 4aeff0f

Browse files
authored
Merge pull request github#12335 from github/alexdenisov/extract-lazy-declarations
Swift: extract lazy declarations
2 parents fe65fb8 + ae7a0c5 commit 4aeff0f

11 files changed

+123
-115
lines changed

swift/extractor/SwiftBuiltinSymbols.h

Lines changed: 0 additions & 73 deletions
This file was deleted.

swift/extractor/SwiftExtractor.cpp

Lines changed: 79 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010

1111
#include "swift/extractor/translators/SwiftVisitor.h"
1212
#include "swift/extractor/infra/TargetDomains.h"
13-
#include "swift/extractor/SwiftBuiltinSymbols.h"
1413
#include "swift/extractor/infra/file/Path.h"
1514
#include "swift/extractor/infra/SwiftLocationExtractor.h"
1615
#include "swift/extractor/infra/SwiftBodyEmissionStrategy.h"
16+
#include "swift/extractor/mangler/SwiftMangler.h"
1717

1818
using namespace codeql;
1919
using namespace std::string_literals;
@@ -43,10 +43,16 @@ static void archiveFile(const SwiftExtractorConfiguration& config, swift::Source
4343
}
4444
}
4545

46-
static fs::path getFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) {
46+
static fs::path getFilename(swift::ModuleDecl& module,
47+
swift::SourceFile* primaryFile,
48+
const swift::Decl* lazyDeclaration) {
4749
if (primaryFile) {
4850
return resolvePath(primaryFile->getFilename());
4951
}
52+
if (lazyDeclaration) {
53+
SwiftMangler mangler;
54+
return mangler.mangledName(*lazyDeclaration);
55+
}
5056
// PCM clang module
5157
if (module.isNonSwiftModule()) {
5258
// Several modules with different names might come from .pcm (clang module) files
@@ -72,60 +78,56 @@ static fs::path getFilename(swift::ModuleDecl& module, swift::SourceFile* primar
7278
return resolvePath(filename);
7379
}
7480

75-
/* The builtin module is special, as it does not publish any top-level declaration
76-
* It creates (and caches) declarations on demand when a lookup is carried out
77-
* (see BuiltinUnit in swift/AST/FileUnit.h for the cache details, and getBuiltinValueDecl in
78-
* swift/AST/Builtins.h for the creation details)
79-
* As we want to create the Builtin trap file once and for all so that it works for other
80-
* extraction runs, rather than collecting what we need we pre-populate the builtin trap with
81-
* what we expect. This list might need thus to be expanded.
82-
* Notice, that while swift/AST/Builtins.def has a list of builtin symbols, it does not contain
83-
* all information required to instantiate builtin variants.
84-
* Other possible approaches:
85-
* * create one trap per builtin declaration when encountered
86-
* * expand the list to all possible builtins (of which there are a lot)
87-
*/
88-
static void getBuiltinDecls(swift::ModuleDecl& builtinModule,
89-
llvm::SmallVector<swift::Decl*>& decls) {
90-
llvm::SmallVector<swift::ValueDecl*> values;
91-
for (auto symbol : swiftBuiltins) {
92-
builtinModule.lookupValue(builtinModule.getASTContext().getIdentifier(symbol),
93-
swift::NLKind::QualifiedLookup, values);
94-
}
95-
decls.insert(decls.end(), values.begin(), values.end());
96-
}
97-
98-
static llvm::SmallVector<swift::Decl*> getTopLevelDecls(swift::ModuleDecl& module,
99-
swift::SourceFile* primaryFile = nullptr) {
100-
llvm::SmallVector<swift::Decl*> ret;
81+
static llvm::SmallVector<const swift::Decl*> getTopLevelDecls(swift::ModuleDecl& module,
82+
swift::SourceFile* primaryFile,
83+
const swift::Decl* lazyDeclaration) {
84+
llvm::SmallVector<const swift::Decl*> ret;
85+
if (lazyDeclaration) {
86+
ret.push_back(lazyDeclaration);
87+
return ret;
88+
}
10189
ret.push_back(&module);
90+
llvm::SmallVector<swift::Decl*> topLevelDecls;
10291
if (primaryFile) {
103-
primaryFile->getTopLevelDecls(ret);
104-
} else if (module.isBuiltinModule()) {
105-
getBuiltinDecls(module, ret);
92+
primaryFile->getTopLevelDecls(topLevelDecls);
10693
} else {
107-
module.getTopLevelDecls(ret);
94+
module.getTopLevelDecls(topLevelDecls);
10895
}
96+
ret.insert(ret.end(), topLevelDecls.data(), topLevelDecls.data() + topLevelDecls.size());
10997
return ret;
11098
}
11199

100+
static TrapType getTrapType(swift::SourceFile* primaryFile, const swift::Decl* lazyDeclaration) {
101+
if (primaryFile) {
102+
return TrapType::source;
103+
}
104+
if (lazyDeclaration) {
105+
return TrapType::lazy_declaration;
106+
}
107+
return TrapType::module;
108+
}
109+
112110
static std::unordered_set<swift::ModuleDecl*> extractDeclarations(
113111
SwiftExtractorState& state,
114112
swift::CompilerInstance& compiler,
115113
swift::ModuleDecl& module,
116-
swift::SourceFile* primaryFile = nullptr) {
117-
auto filename = getFilename(module, primaryFile);
114+
swift::SourceFile* primaryFile,
115+
const swift::Decl* lazyDeclaration) {
116+
auto filename = getFilename(module, primaryFile, lazyDeclaration);
118117
if (primaryFile) {
119118
state.sourceFiles.push_back(filename);
120119
}
121120

122121
// The extractor can be called several times from different processes with
123122
// the same input file(s). Using `TargetFile` the first process will win, and the following
124123
// will just skip the work
125-
const auto trapType = primaryFile ? TrapType::source : TrapType::module;
124+
const auto trapType = getTrapType(primaryFile, lazyDeclaration);
126125
auto trap = createTargetTrapDomain(state, filename, trapType);
127126
if (!trap) {
128127
// another process arrived first, nothing to do for us
128+
if (lazyDeclaration) {
129+
state.emittedDeclarations.insert(lazyDeclaration);
130+
}
129131
return {};
130132
}
131133

@@ -143,9 +145,10 @@ static std::unordered_set<swift::ModuleDecl*> extractDeclarations(
143145

144146
SwiftLocationExtractor locationExtractor(*trap);
145147
locationExtractor.emitFile(primaryFile);
146-
SwiftBodyEmissionStrategy bodyEmissionStrategy(module, primaryFile);
147-
SwiftVisitor visitor(compiler.getSourceMgr(), *trap, locationExtractor, bodyEmissionStrategy);
148-
auto topLevelDecls = getTopLevelDecls(module, primaryFile);
148+
SwiftBodyEmissionStrategy bodyEmissionStrategy(module, primaryFile, lazyDeclaration);
149+
SwiftVisitor visitor(compiler.getSourceMgr(), state, *trap, locationExtractor,
150+
bodyEmissionStrategy);
151+
auto topLevelDecls = getTopLevelDecls(module, primaryFile, lazyDeclaration);
149152
for (auto decl : topLevelDecls) {
150153
visitor.extract(decl);
151154
}
@@ -198,10 +201,12 @@ void codeql::extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstan
198201
continue;
199202
}
200203
archiveFile(state.configuration, *sourceFile);
201-
encounteredModules = extractDeclarations(state, compiler, *module, sourceFile);
204+
encounteredModules =
205+
extractDeclarations(state, compiler, *module, sourceFile, /*lazy declaration*/ nullptr);
202206
}
203207
if (!isFromSourceFile) {
204-
encounteredModules = extractDeclarations(state, compiler, *module);
208+
encounteredModules = extractDeclarations(state, compiler, *module, /*source file*/ nullptr,
209+
/*lazy declaration*/ nullptr);
205210
}
206211
for (auto encountered : encounteredModules) {
207212
if (state.encounteredModules.count(encountered) == 0) {
@@ -211,3 +216,37 @@ void codeql::extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstan
211216
}
212217
}
213218
}
219+
220+
static void cleanupPendingDeclarations(SwiftExtractorState& state) {
221+
std::vector<const swift::Decl*> worklist(std::begin(state.pendingDeclarations),
222+
std::end(state.pendingDeclarations));
223+
for (auto decl : worklist) {
224+
if (state.emittedDeclarations.count(decl)) {
225+
state.pendingDeclarations.erase(decl);
226+
}
227+
}
228+
}
229+
230+
static void extractLazy(SwiftExtractorState& state, swift::CompilerInstance& compiler) {
231+
cleanupPendingDeclarations(state);
232+
std::vector<const swift::Decl*> worklist(std::begin(state.pendingDeclarations),
233+
std::end(state.pendingDeclarations));
234+
for (auto pending : worklist) {
235+
extractDeclarations(state, compiler, *pending->getModuleContext(), /*source file*/ nullptr,
236+
pending);
237+
}
238+
}
239+
240+
void codeql::extractExtractLazyDeclarations(SwiftExtractorState& state,
241+
swift::CompilerInstance& compiler) {
242+
// Just in case
243+
const int upperBound = 100;
244+
int iteration = 0;
245+
while (!state.pendingDeclarations.empty() && iteration++ < upperBound) {
246+
extractLazy(state, compiler);
247+
}
248+
if (iteration >= upperBound) {
249+
std::cerr << "Swift extractor reached upper bound while extracting lazy declarations\n";
250+
abort();
251+
}
252+
}

swift/extractor/SwiftExtractor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
namespace codeql {
99
void extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstance& compiler);
10+
void extractExtractLazyDeclarations(SwiftExtractorState& state, swift::CompilerInstance& compiler);
1011
} // namespace codeql

swift/extractor/config/SwiftExtractorState.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ struct SwiftExtractorState {
2424

2525
// The path for the modules outputted by the underlying frontend run, ignoring path redirection
2626
std::vector<std::filesystem::path> originalOutputModules;
27+
28+
// All lazy named declarations that were already emitted
29+
std::unordered_set<const swift::Decl*> emittedDeclarations;
30+
31+
// Lazy named declarations that were not yet emitted and will be emitted each one separately
32+
std::unordered_set<const swift::Decl*> pendingDeclarations;
2733
};
2834

2935
} // namespace codeql

swift/extractor/infra/SwiftBodyEmissionStrategy.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ bool SwiftBodyEmissionStrategy::shouldEmitDeclBody(const swift::Decl& decl) {
1616
if (module != &currentModule) {
1717
return false;
1818
}
19+
if (currentLazyDeclaration && currentLazyDeclaration != &decl) {
20+
return false;
21+
}
1922
// ModuleDecl is a special case: if it passed the previous test, it is the current module
2023
// but it never has a source file, so we short circuit to emit it in any case
2124
if (!currentPrimarySourceFile || decl.getKind() == swift::DeclKind::Module) {

swift/extractor/infra/SwiftBodyEmissionStrategy.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ namespace codeql {
88
class SwiftBodyEmissionStrategy {
99
public:
1010
SwiftBodyEmissionStrategy(swift::ModuleDecl& currentModule,
11-
swift::SourceFile* currentPrimarySourceFile)
12-
: currentModule(currentModule), currentPrimarySourceFile(currentPrimarySourceFile) {}
11+
swift::SourceFile* currentPrimarySourceFile,
12+
const swift::Decl* currentLazyDeclaration)
13+
: currentModule(currentModule),
14+
currentPrimarySourceFile(currentPrimarySourceFile),
15+
currentLazyDeclaration(currentLazyDeclaration) {}
1316
bool shouldEmitDeclBody(const swift::Decl& decl);
1417

1518
private:
1619
swift::ModuleDecl& currentModule;
1720
swift::SourceFile* currentPrimarySourceFile;
21+
const swift::Decl* currentLazyDeclaration;
1822
};
1923

2024
} // namespace codeql

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "swift/extractor/trap/generated/TrapClasses.h"
1313
#include "swift/extractor/infra/SwiftLocationExtractor.h"
1414
#include "swift/extractor/infra/SwiftBodyEmissionStrategy.h"
15+
#include "swift/extractor/config/SwiftExtractorState.h"
1516

1617
namespace codeql {
1718

@@ -45,10 +46,12 @@ class SwiftDispatcher {
4546
// all references and pointers passed as parameters to this constructor are supposed to outlive
4647
// the SwiftDispatcher
4748
SwiftDispatcher(const swift::SourceManager& sourceManager,
49+
SwiftExtractorState& state,
4850
TrapDomain& trap,
4951
SwiftLocationExtractor& locationExtractor,
5052
SwiftBodyEmissionStrategy& bodyEmissionStrategy)
5153
: sourceManager{sourceManager},
54+
state{state},
5255
trap{trap},
5356
locationExtractor{locationExtractor},
5457
bodyEmissionStrategy{bodyEmissionStrategy} {}
@@ -248,7 +251,23 @@ class SwiftDispatcher {
248251
locationExtractor.attachLocation(sourceManager, comment, entry.id);
249252
}
250253

254+
void extractedDeclaration(const swift::Decl& decl) {
255+
if (isLazyDeclaration(decl)) {
256+
state.emittedDeclarations.insert(&decl);
257+
}
258+
}
259+
void skippedDeclaration(const swift::Decl& decl) {
260+
if (isLazyDeclaration(decl)) {
261+
state.pendingDeclarations.insert(&decl);
262+
}
263+
}
264+
251265
private:
266+
bool isLazyDeclaration(const swift::Decl& decl) {
267+
swift::ModuleDecl* module = decl.getModuleContext();
268+
return module->isBuiltinModule() || module->getName().str() == "__ObjC";
269+
}
270+
252271
template <typename T, typename = void>
253272
struct HasSize : std::false_type {};
254273

@@ -302,6 +321,7 @@ class SwiftDispatcher {
302321
virtual void visit(const swift::CapturedValue* capture) = 0;
303322

304323
const swift::SourceManager& sourceManager;
324+
SwiftExtractorState& state;
305325
TrapDomain& trap;
306326
Store store;
307327
SwiftLocationExtractor& locationExtractor;

swift/extractor/infra/TargetDomains.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ static const char* typeToStr(TrapType type) {
1313
return "invocations";
1414
case TrapType::linkage:
1515
return "linkage";
16+
case TrapType::lazy_declaration:
17+
return "lazy_decls";
1618
default:
1719
return "";
1820
}

swift/extractor/infra/TargetDomains.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ enum class TrapType {
1212
module,
1313
invocation,
1414
linkage,
15+
lazy_declaration,
1516
};
1617

1718
std::filesystem::path getTrapPath(const SwiftExtractorState& state,

swift/extractor/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class Observer : public swift::FrontendObserver {
9292
void performedSemanticAnalysis(swift::CompilerInstance& compiler) override {
9393
codeql::extractSwiftFiles(state, compiler);
9494
codeql::extractSwiftInvocation(state, compiler, invocationTrap);
95+
codeql::extractExtractLazyDeclarations(state, compiler);
9596
}
9697

9798
private:

0 commit comments

Comments
 (0)