Skip to content

Commit 9d80a43

Browse files
authored
Merge pull request github#12500 from github/redsun82/swift-dispatcher-rework
Swift: rework fetching and dispatching
2 parents 6110b7a + 125aab8 commit 9d80a43

23 files changed

+558
-456
lines changed

swift/extractor/BUILD.bazel

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ swift_cc_binary(
1212
"//swift/extractor/config",
1313
"//swift/extractor/infra",
1414
"//swift/extractor/invocation",
15-
"//swift/extractor/mangler",
1615
"//swift/extractor/remapping",
1716
"//swift/extractor/translators",
1817
"//swift/third_party/swift-llvm-support",

swift/extractor/SwiftExtractor.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,26 @@ static void archiveFile(const SwiftExtractorConfiguration& config, swift::Source
4444
}
4545
}
4646

47+
// TODO: This should be factored out/replaced with simplified version of custom mangling
48+
static std::string mangledDeclName(const swift::ValueDecl& decl) {
49+
std::string_view moduleName = decl.getModuleContext()->getRealName().str();
50+
// ASTMangler::mangleAnyDecl crashes when called on `ModuleDecl`
51+
if (decl.getKind() == swift::DeclKind::Module) {
52+
return std::string{moduleName};
53+
}
54+
swift::Mangle::ASTMangler mangler;
55+
if (decl.getKind() == swift::DeclKind::TypeAlias) {
56+
// In cases like this (when coming from PCM)
57+
// typealias CFXMLTree = CFTree
58+
// typealias CFXMLTreeRef = CFXMLTree
59+
// mangleAnyDecl mangles both CFXMLTree and CFXMLTreeRef into 'So12CFXMLTreeRefa'
60+
// which is not correct and causes inconsistencies. mangleEntity makes these two distinct
61+
// prefix adds a couple of special symbols, we don't necessary need them
62+
return mangler.mangleEntity(&decl);
63+
}
64+
return mangler.mangleAnyDecl(&decl, /* prefix = */ false);
65+
}
66+
4767
static fs::path getFilename(swift::ModuleDecl& module,
4868
swift::SourceFile* primaryFile,
4969
const swift::Decl* lazyDeclaration) {
@@ -52,15 +72,15 @@ static fs::path getFilename(swift::ModuleDecl& module,
5272
}
5373
if (lazyDeclaration) {
5474
// this code will be thrown away in the near future
55-
SwiftMangler mangler;
56-
auto mangled = mangler.mangledName(*lazyDeclaration);
75+
auto decl = llvm::dyn_cast<swift::ValueDecl>(lazyDeclaration);
76+
assert(decl);
77+
auto mangled = mangledDeclName(*decl);
5778
// mangled name can be too long to use as a file name, so we can't use it directly
5879
mangled = picosha2::hash256_hex_string(mangled);
5980
std::string ret;
6081
ret += module.getRealName().str();
6182
ret += '_';
62-
// lazyDeclaration must be a ValueDecl, as already asserted in SwiftMangler::mangledName
63-
ret += llvm::cast<swift::ValueDecl>(lazyDeclaration)->getBaseName().userFacingName();
83+
ret += decl->getBaseName().userFacingName();
6484
ret += '_';
6585
// half a SHA2 is enough
6686
ret += std::string_view(mangled).substr(0, mangled.size() / 2);

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 102 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
#include <swift/Basic/SourceManager.h>
77
#include <swift/Parse/Token.h>
88

9-
#include "swift/extractor/trap/TrapLabelStore.h"
109
#include "swift/extractor/trap/TrapDomain.h"
1110
#include "swift/extractor/infra/SwiftTagTraits.h"
1211
#include "swift/extractor/trap/generated/TrapClasses.h"
1312
#include "swift/extractor/infra/SwiftLocationExtractor.h"
1413
#include "swift/extractor/infra/SwiftBodyEmissionStrategy.h"
14+
#include "swift/extractor/infra/SwiftMangledName.h"
1515
#include "swift/extractor/config/SwiftExtractorState.h"
1616
#include "swift/extractor/infra/log/SwiftLogging.h"
1717

@@ -24,24 +24,31 @@ namespace codeql {
2424
// node (AST nodes that are not types: declarations, statements, expressions, etc.).
2525
class SwiftDispatcher {
2626
// types to be supported by assignNewLabel/fetchLabel need to be listed here
27-
using Store = TrapLabelStore<const swift::Decl*,
28-
const swift::Stmt*,
29-
const swift::StmtCondition*,
30-
const swift::StmtConditionElement*,
31-
const swift::CaseLabelItem*,
32-
const swift::Expr*,
33-
const swift::Pattern*,
34-
const swift::TypeRepr*,
35-
const swift::TypeBase*,
36-
const swift::CapturedValue*,
37-
const swift::PoundAvailableInfo*,
38-
const swift::AvailabilitySpec*>;
27+
using Handle = std::variant<const swift::Decl*,
28+
const swift::Stmt*,
29+
const swift::StmtCondition*,
30+
const swift::StmtConditionElement*,
31+
const swift::CaseLabelItem*,
32+
const swift::Expr*,
33+
const swift::Pattern*,
34+
const swift::TypeRepr*,
35+
const swift::TypeBase*,
36+
const swift::CapturedValue*,
37+
const swift::PoundAvailableInfo*,
38+
const swift::AvailabilitySpec*>;
3939

4040
template <typename E>
41-
static constexpr bool IsStorable = std::is_constructible_v<Store::Handle, const E&>;
41+
static constexpr bool IsFetchable = std::is_constructible_v<Handle, const E&>;
4242

4343
template <typename E>
44-
static constexpr bool IsLocatable = std::is_base_of_v<LocatableTag, TrapTagOf<E>>;
44+
static constexpr bool IsLocatable =
45+
std::is_base_of_v<LocatableTag, TrapTagOf<E>> && !std::is_base_of_v<TypeTag, TrapTagOf<E>>;
46+
47+
template <typename E>
48+
static constexpr bool IsDeclPointer = std::is_convertible_v<E, const swift::Decl*>;
49+
50+
template <typename E>
51+
static constexpr bool IsTypePointer = std::is_convertible_v<E, const swift::TypeBase*>;
4552

4653
public:
4754
// all references and pointers passed as parameters to this constructor are supposed to outlive
@@ -112,7 +119,7 @@ class SwiftDispatcher {
112119
TrapLabel<UnspecifiedElementTag> emitUnspecified(std::optional<TrapLabel<ElementTag>>&& parent,
113120
const char* property,
114121
int index) {
115-
UnspecifiedElement entry{trap.createLabel<UnspecifiedElementTag>()};
122+
UnspecifiedElement entry{trap.createTypedLabel<UnspecifiedElementTag>()};
116123
entry.error = "element was unspecified by the extractor";
117124
entry.parent = std::move(parent);
118125
entry.property = property;
@@ -135,35 +142,22 @@ class SwiftDispatcher {
135142
// This method gives a TRAP label for already emitted AST node.
136143
// If the AST node was not emitted yet, then the emission is dispatched to a corresponding
137144
// visitor (see `visit(T *)` methods below).
138-
template <typename E, typename... Args, std::enable_if_t<IsStorable<E>>* = nullptr>
139-
TrapLabelOf<E> fetchLabel(const E& e, Args&&... args) {
145+
template <typename E, std::enable_if_t<IsFetchable<E>>* = nullptr>
146+
TrapLabelOf<E> fetchLabel(const E& e, swift::Type type = {}) {
140147
if constexpr (std::is_constructible_v<bool, const E&>) {
141148
if (!e) {
142149
// this will be treated on emission
143150
return undefined_label;
144151
}
145152
}
146-
// this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might
147-
// end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel`
148-
// only after having called `assignNewLabel` on `e`.
149-
assert(std::holds_alternative<std::monostate>(waitingForNewLabel) &&
150-
"fetchLabel called before assignNewLabel");
151-
if (auto l = store.get(e)) {
152-
return *l;
153+
auto& stored = store[e];
154+
if (!stored.valid()) {
155+
auto inserted = fetching.insert(e);
156+
assert(inserted.second && "detected infinite fetchLabel recursion");
157+
stored = createLabel(e, type);
158+
fetching.erase(e);
153159
}
154-
waitingForNewLabel = e;
155-
// TODO: add tracing logs for visited stuff, maybe within the translators?
156-
visit(e, std::forward<Args>(args)...);
157-
Log::flush();
158-
// TODO when everything is moved to structured C++ classes, this should be moved to createEntry
159-
if (auto l = store.get(e)) {
160-
if constexpr (IsLocatable<E>) {
161-
locationExtractor.attachLocation(sourceManager, e, *l);
162-
}
163-
return *l;
164-
}
165-
assert(!"assignNewLabel not called during visit");
166-
return {};
160+
return TrapLabelOf<E>::unsafeCreateFromUntyped(stored);
167161
}
168162

169163
// convenience `fetchLabel` overload for `swift::Type` (which is just a wrapper for
@@ -174,39 +168,28 @@ class SwiftDispatcher {
174168
return fetchLabelFromUnion<AstNodeTag>(node);
175169
}
176170

177-
template <typename E, std::enable_if_t<IsStorable<E*>>* = nullptr>
171+
template <typename E, std::enable_if_t<IsFetchable<E*>>* = nullptr>
178172
TrapLabelOf<E> fetchLabel(const E& e) {
179173
return fetchLabel(&e);
180174
}
181175

182-
// Due to the lazy emission approach, we must assign a label to a corresponding AST node before
183-
// it actually gets emitted to handle recursive cases such as recursive calls, or recursive type
184-
// declarations
185-
template <typename E, typename... Args, std::enable_if_t<IsStorable<E>>* = nullptr>
186-
TrapLabel<ConcreteTrapTagOf<E>> assignNewLabel(const E& e, Args&&... args) {
187-
assert(waitingForNewLabel == Store::Handle{e} && "assignNewLabel called on wrong entity");
188-
auto label = trap.createLabel<ConcreteTrapTagOf<E>>(std::forward<Args>(args)...);
189-
store.insert(e, label);
190-
waitingForNewLabel = std::monostate{};
191-
return label;
192-
}
193-
194-
template <typename E, typename... Args, std::enable_if_t<IsStorable<E*>>* = nullptr>
195-
TrapLabel<ConcreteTrapTagOf<E>> assignNewLabel(const E& e, Args&&... args) {
196-
return assignNewLabel(&e, std::forward<Args>(args)...);
197-
}
198-
199176
// convenience methods for structured C++ creation
200-
template <typename E, typename... Args>
201-
auto createEntry(const E& e, Args&&... args) {
202-
return TrapClassOf<E>{assignNewLabel(e, std::forward<Args>(args)...)};
177+
template <typename E>
178+
auto createEntry(const E& e) {
179+
auto found = store.find(&e);
180+
assert(found != store.end() && "createEntry called on non-fetched label");
181+
auto label = TrapLabel<ConcreteTrapTagOf<E>>::unsafeCreateFromUntyped(found->second);
182+
if constexpr (IsLocatable<E>) {
183+
locationExtractor.attachLocation(sourceManager, e, label);
184+
}
185+
return TrapClassOf<E>{label};
203186
}
204187

205188
// used to create a new entry for entities that should not be cached
206189
// an example is swift::Argument, that are created on the fly and thus have no stable pointer
207-
template <typename E, typename... Args>
208-
auto createUncachedEntry(const E& e, Args&&... args) {
209-
auto label = trap.createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
190+
template <typename E>
191+
auto createUncachedEntry(const E& e) {
192+
auto label = trap.createTypedLabel<TrapTagOf<E>>();
210193
locationExtractor.attachLocation(sourceManager, &e, label);
211194
return TrapClassOf<E>{label};
212195
}
@@ -243,31 +226,65 @@ class SwiftDispatcher {
243226
trap.debug(args...);
244227
}
245228

246-
bool shouldEmitDeclBody(const swift::Decl& decl) {
247-
encounteredModules.insert(decl.getModuleContext());
248-
return bodyEmissionStrategy.shouldEmitDeclBody(decl);
249-
}
250-
251229
void emitComment(swift::Token& comment) {
252-
CommentsTrap entry{trap.createLabel<CommentTag>(), comment.getRawText().str()};
230+
CommentsTrap entry{trap.createTypedLabel<CommentTag>(), comment.getRawText().str()};
253231
trap.emit(entry);
254232
locationExtractor.attachLocation(sourceManager, comment, entry.id);
255233
}
256234

257-
void extractedDeclaration(const swift::Decl& decl) {
235+
protected:
236+
void visitPending() {
237+
while (!toBeVisited.empty()) {
238+
auto [next, type] = toBeVisited.back();
239+
toBeVisited.pop_back();
240+
// TODO: add tracing logs for visited stuff, maybe within the translators?
241+
std::visit([this, type = type](const auto* e) { visit(e, type); }, next);
242+
}
243+
}
244+
245+
private:
246+
template <typename E>
247+
UntypedTrapLabel createLabel(const E& e, swift::Type type) {
248+
if constexpr (IsDeclPointer<E> || IsTypePointer<E>) {
249+
if (auto mangledName = name(e)) {
250+
if (shouldVisit(e)) {
251+
toBeVisited.emplace_back(e, type);
252+
}
253+
return trap.createLabel(mangledName);
254+
}
255+
}
256+
// we always need to visit unnamed things
257+
toBeVisited.emplace_back(e, type);
258+
return trap.createLabel();
259+
}
260+
261+
template <typename E>
262+
bool shouldVisit(const E& e) {
263+
if constexpr (IsDeclPointer<E>) {
264+
encounteredModules.insert(e->getModuleContext());
265+
if (bodyEmissionStrategy.shouldEmitDeclBody(*e)) {
266+
extractedDeclaration(e);
267+
return true;
268+
}
269+
skippedDeclaration(e);
270+
return false;
271+
}
272+
return true;
273+
}
274+
275+
void extractedDeclaration(const swift::Decl* decl) {
258276
if (isLazyDeclaration(decl)) {
259-
state.emittedDeclarations.insert(&decl);
277+
state.emittedDeclarations.insert(decl);
260278
}
261279
}
262-
void skippedDeclaration(const swift::Decl& decl) {
280+
void skippedDeclaration(const swift::Decl* decl) {
263281
if (isLazyDeclaration(decl)) {
264-
state.pendingDeclarations.insert(&decl);
282+
state.pendingDeclarations.insert(decl);
265283
}
266284
}
267285

268-
private:
269-
bool isLazyDeclaration(const swift::Decl& decl) {
270-
swift::ModuleDecl* module = decl.getModuleContext();
286+
static bool isLazyDeclaration(const swift::Decl* decl) {
287+
swift::ModuleDecl* module = decl->getModuleContext();
271288
return module->isBuiltinModule() || module->getName().str() == "__ObjC" ||
272289
module->isNonSwiftModule();
273290
}
@@ -311,6 +328,8 @@ class SwiftDispatcher {
311328
return false;
312329
}
313330

331+
virtual SwiftMangledName name(const swift::Decl* decl) = 0;
332+
virtual SwiftMangledName name(const swift::TypeBase* type) = 0;
314333
virtual void visit(const swift::Decl* decl) = 0;
315334
virtual void visit(const swift::Stmt* stmt) = 0;
316335
virtual void visit(const swift::StmtCondition* cond) = 0;
@@ -324,13 +343,19 @@ class SwiftDispatcher {
324343
virtual void visit(const swift::TypeBase* type) = 0;
325344
virtual void visit(const swift::CapturedValue* capture) = 0;
326345

346+
template <typename T, std::enable_if<!std::is_base_of_v<swift::TypeRepr, T>>* = nullptr>
347+
void visit(const T* e, swift::Type) {
348+
visit(e);
349+
}
350+
327351
const swift::SourceManager& sourceManager;
328352
SwiftExtractorState& state;
329353
TrapDomain& trap;
330-
Store store;
354+
std::unordered_map<Handle, UntypedTrapLabel> store;
355+
std::unordered_set<Handle> fetching;
356+
std::vector<std::pair<Handle, swift::Type>> toBeVisited;
331357
SwiftLocationExtractor& locationExtractor;
332358
SwiftBodyEmissionStrategy& bodyEmissionStrategy;
333-
Store::Handle waitingForNewLabel{std::monostate{}};
334359
std::unordered_set<swift::ModuleDecl*> encounteredModules;
335360
Logger logger{"dispatcher"};
336361
};

0 commit comments

Comments
 (0)