Skip to content

Commit 4909ee5

Browse files
committed
[Macros] Implement unqualified lookup for global macro-expanded peer declarations.
1 parent 5fe47bf commit 4909ee5

File tree

4 files changed

+235
-7
lines changed

4 files changed

+235
-7
lines changed

include/swift/AST/Decl.h

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8319,10 +8319,20 @@ class PostfixOperatorDecl : public OperatorDecl {
83198319
}
83208320
};
83218321

8322-
/// Represents a missing declaration in the source code. This
8323-
/// is used for parser recovery, e.g. when parsing a floating
8324-
/// attribute list.
8325-
class MissingDecl: public Decl {
8322+
/// Represents a missing declaration in the source code.
8323+
///
8324+
/// This is used for parser recovery, e.g. when parsing a floating
8325+
/// attribute list, and to represent placeholders for unexpanded
8326+
/// declarations generated by macros.
8327+
class MissingDecl : public Decl {
8328+
/// If this missing decl represents an unexpanded peer generated
8329+
/// by a macro, \c unexpandedPeer contains the macro custom attribute
8330+
/// and the declaration the macro is attached to.
8331+
struct {
8332+
CustomAttr *macroAttr = nullptr;
8333+
ValueDecl *attachedTo = nullptr;
8334+
} unexpandedPeer;
8335+
83268336
/// The location that the decl would be if it wasn't missing.
83278337
SourceLoc Loc;
83288338

@@ -8341,6 +8351,25 @@ class MissingDecl: public Decl {
83418351

83428352
SourceRange getSourceRange() const { return SourceRange(Loc); }
83438353

8354+
static MissingDecl *
8355+
forUnexpandedPeer(CustomAttr *macro, ValueDecl *attachedTo) {
8356+
auto &ctx = attachedTo->getASTContext();
8357+
auto *dc = attachedTo->getDeclContext();
8358+
auto *missing = new (ctx) MissingDecl(dc);
8359+
8360+
missing->unexpandedPeer.macroAttr = macro;
8361+
missing->unexpandedPeer.attachedTo = attachedTo;
8362+
8363+
return missing;
8364+
}
8365+
8366+
using ExpandedPeerCallback = llvm::function_ref<void(ValueDecl *)>;
8367+
void forEachExpandedPeer(ExpandedPeerCallback callback);
8368+
8369+
SourceRange getSourceRange() const {
8370+
return SourceRange();
8371+
}
8372+
83448373
static bool classof(const Decl *D) {
83458374
return D->getKind() == DeclKind::Missing;
83468375
}
@@ -8446,6 +8475,11 @@ class MacroDecl : public GenericContext, public ValueDecl {
84468475
/// Retrieve the attribute that declared the given macro role.
84478476
const MacroRoleAttr *getMacroRoleAttr(MacroRole role) const;
84488477

8478+
/// Populate the \c names vector with the decl names introduced
8479+
/// by a given role of this macro.
8480+
void getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
8481+
SmallVectorImpl<DeclName> &names) const;
8482+
84498483
/// Retrieve the definition of this macro.
84508484
MacroDefinition getDefinition() const;
84518485

lib/AST/Decl.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9974,6 +9974,28 @@ MacroRoles swift::getAttachedMacroRoles() {
99749974
return attachedMacroRoles;
99759975
}
99769976

9977+
void
9978+
MissingDecl::forEachExpandedPeer(ExpandedPeerCallback callback) {
9979+
auto *macro = unexpandedPeer.macroAttr;
9980+
auto *attachedTo = unexpandedPeer.attachedTo;
9981+
if (!macro || !attachedTo)
9982+
return;
9983+
9984+
attachedTo->visitAuxiliaryDecls(
9985+
[&](Decl *auxiliaryDecl) {
9986+
auto *sf = auxiliaryDecl->getInnermostDeclContext()->getParentSourceFile();
9987+
auto *macroAttr = sf->getAttachedMacroAttribute();
9988+
if (macroAttr != unexpandedPeer.macroAttr)
9989+
return;
9990+
9991+
auto *value = dyn_cast<ValueDecl>(auxiliaryDecl);
9992+
if (!value)
9993+
return;
9994+
9995+
callback(value);
9996+
});
9997+
}
9998+
99779999
MacroDecl::MacroDecl(
997810000
SourceLoc macroLoc, DeclName name, SourceLoc nameLoc,
997910001
GenericParamList *genericParams,
@@ -10027,7 +10049,68 @@ const MacroRoleAttr *MacroDecl::getMacroRoleAttr(MacroRole role) const {
1002710049
for (auto attr : getAttrs().getAttributes<MacroRoleAttr>())
1002810050
if (attr->getMacroRole() == role)
1002910051
return attr;
10030-
llvm_unreachable("Macro role not declared for this MacroDecl");
10052+
10053+
return nullptr;
10054+
}
10055+
10056+
void MacroDecl::getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
10057+
SmallVectorImpl<DeclName> &names) const {
10058+
ASTContext &ctx = getASTContext();
10059+
auto *attr = getMacroRoleAttr(role);
10060+
if (!attr)
10061+
return;
10062+
10063+
for (auto expandedName : attr->getNames()) {
10064+
switch (expandedName.getKind()) {
10065+
case MacroIntroducedDeclNameKind::Named: {
10066+
names.push_back(DeclName(expandedName.getIdentifier()));
10067+
break;
10068+
}
10069+
10070+
case MacroIntroducedDeclNameKind::Overloaded: {
10071+
if (!attachedTo)
10072+
break;
10073+
10074+
names.push_back(attachedTo->getBaseName());
10075+
break;
10076+
}
10077+
10078+
case MacroIntroducedDeclNameKind::Prefixed: {
10079+
if (!attachedTo)
10080+
break;
10081+
10082+
auto baseName = attachedTo->getBaseName();
10083+
std::string prefixedName;
10084+
llvm::raw_string_ostream out(prefixedName);
10085+
out << expandedName.getIdentifier();
10086+
out << baseName.getIdentifier();
10087+
10088+
Identifier nameId = ctx.getIdentifier(prefixedName);
10089+
names.push_back(DeclName(nameId));
10090+
break;
10091+
}
10092+
10093+
case MacroIntroducedDeclNameKind::Suffixed: {
10094+
if (!attachedTo)
10095+
break;
10096+
10097+
auto baseName = attachedTo->getBaseName();
10098+
std::string suffixedName;
10099+
llvm::raw_string_ostream out(suffixedName);
10100+
out << baseName.getIdentifier();
10101+
out << expandedName.getIdentifier();
10102+
10103+
Identifier nameId = ctx.getIdentifier(suffixedName);
10104+
names.push_back(DeclName(nameId));
10105+
break;
10106+
}
10107+
10108+
case MacroIntroducedDeclNameKind::Arbitrary:
10109+
// Only freestanding and member macros can have arbitrary names.
10110+
assert(role == MacroRole::Member || role == MacroRole::Declaration);
10111+
break;
10112+
}
10113+
}
1003110114
}
1003210115

1003310116
MacroDefinition MacroDecl::getDefinition() const {

lib/AST/Module.cpp

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ class swift::SourceLookupCache {
170170
void addToUnqualifiedLookupCache(Range decls, bool onlyOperators);
171171
template<typename Range>
172172
void addToMemberCache(Range decls);
173+
174+
using AuxiliaryDeclMap = llvm::DenseMap<DeclName, TinyPtrVector<MissingDecl *>>;
175+
AuxiliaryDeclMap TopLevelAuxiliaryDecls;
176+
SmallVector<ValueDecl *, 4> MayHaveAuxiliaryDecls;
177+
void populateAuxiliaryDeclCache();
178+
173179
public:
174180
SourceLookupCache(const SourceFile &SF);
175181
SourceLookupCache(const ModuleDecl &Mod);
@@ -234,6 +240,10 @@ void SourceLookupCache::addToUnqualifiedLookupCache(Range decls,
234240
if (onlyOperators ? VD->isOperator() : VD->hasName()) {
235241
// Cache the value under both its compound name and its full name.
236242
TopLevelValues.add(VD);
243+
244+
if (VD->getAttrs().hasAttribute<CustomAttr>()) {
245+
MayHaveAuxiliaryDecls.push_back(VD);
246+
}
237247
}
238248
}
239249

@@ -309,6 +319,56 @@ void SourceLookupCache::addToMemberCache(Range decls) {
309319
}
310320
}
311321

322+
void SourceLookupCache::populateAuxiliaryDeclCache() {
323+
for (auto *decl : MayHaveAuxiliaryDecls) {
324+
auto *dc = decl->getDeclContext();
325+
326+
// Gather macro-introduced peer names.
327+
llvm::SmallDenseMap<CustomAttr *, llvm::SmallVector<DeclName, 2>> introducedNames;
328+
329+
// This code deliberately avoids `forEachAttachedMacro`, because it
330+
// will perform overload resolution and possibly invoke unqualified
331+
// lookup for macro arguments, which will recursively populate the
332+
// auxiliary decl cache and cause request cycles.
333+
//
334+
// We do not need a fully resolved macro until expansion. Instead, we
335+
// conservatively consider peer names for all macro declarations with a
336+
// custom attribute name. Unqualified lookup for that name will later
337+
// invoke expansion of the macro, and will yield no results if the resolved
338+
// macro does not produce the requested name, so the only impact is possibly
339+
// expanding earlier than needed / unnecessarily looking in the top-level
340+
// auxiliary decl cache.
341+
for (auto attrConst : decl->getSemanticAttrs().getAttributes<CustomAttr>()) {
342+
auto *attr = const_cast<CustomAttr *>(attrConst);
343+
UnresolvedMacroReference macroRef(attr);
344+
auto moduleScopeDC = dc->getModuleScopeContext();
345+
ASTContext &ctx = moduleScopeDC->getASTContext();
346+
UnqualifiedLookupDescriptor descriptor(macroRef.getMacroName(), moduleScopeDC);
347+
auto lookup = evaluateOrDefault(
348+
ctx.evaluator, UnqualifiedLookupRequest{descriptor}, {});
349+
for (const auto &found : lookup.allResults()) {
350+
if (auto macro = dyn_cast<MacroDecl>(found.getValueDecl())) {
351+
macro->getIntroducedNames(MacroRole::Peer,
352+
decl, introducedNames[attr]);
353+
}
354+
}
355+
}
356+
357+
// Add macro-introduced names to the top-level auxiliary decl cache as
358+
// unexpanded peer decls represented by a MissingDecl.
359+
for (auto macroNames : introducedNames) {
360+
auto *macroAttr = macroNames.getFirst();
361+
for (auto name : macroNames.getSecond()) {
362+
auto *placeholder =
363+
MissingDecl::forUnexpandedPeer(macroAttr, decl);
364+
name.addToLookupTable(TopLevelAuxiliaryDecls, placeholder);
365+
}
366+
}
367+
}
368+
369+
MayHaveAuxiliaryDecls.clear();
370+
}
371+
312372
/// Populate our cache on the first name lookup.
313373
SourceLookupCache::SourceLookupCache(const SourceFile &SF) {
314374
FrontendStatsTracer tracer(SF.getASTContext().Stats,
@@ -336,9 +396,35 @@ void SourceLookupCache::lookupValue(DeclName Name, NLKind LookupKind,
336396
auto I = TopLevelValues.find(Name);
337397
if (I == TopLevelValues.end()) return;
338398

399+
bool foundMacros = false;
339400
Result.reserve(I->second.size());
340-
for (ValueDecl *Elt : I->second)
401+
for (ValueDecl *Elt : I->second) {
341402
Result.push_back(Elt);
403+
foundMacros = isa<MacroDecl>(Elt);
404+
}
405+
406+
// If none of the results are macro decls, look into peers.
407+
//
408+
// FIXME: This approach is an approximation for whether lookup is attempting
409+
// to find macro declarations, and it may cause cycles for invalid macro
410+
// references. We need a more principled way to determine whether we're looking
411+
// for a macro and should not look into auxiliary decls during lookup.
412+
//
413+
// We also need to not consider auxiliary decls if we're doing lookup from
414+
// inside a macro argument at module scope.
415+
if (!foundMacros) {
416+
populateAuxiliaryDeclCache();
417+
auto I = TopLevelAuxiliaryDecls.find(Name);
418+
if (I == TopLevelAuxiliaryDecls.end()) return;
419+
420+
for (auto *unexpandedDecl : I->second) {
421+
// Add expanded peers to the result.
422+
unexpandedDecl->forEachExpandedPeer(
423+
[&](ValueDecl *expandedPeer) {
424+
Result.push_back(expandedPeer);
425+
});
426+
}
427+
}
342428
}
343429

344430
void SourceLookupCache::getPrecedenceGroups(

test/Macros/macro_expand_peers.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// FIXME: Swift parser is not enabled on Linux CI yet.
99
// REQUIRES: OS=macosx
1010

11-
@attached(peer)
11+
@attached(peer, names: overloaded)
1212
macro addCompletionHandler() = #externalMacro(module: "MacroDefinition", type: "AddCompletionHandler")
1313

1414
struct S {
@@ -23,8 +23,33 @@ struct S {
2323
// CHECK-DUMP: completionHandler(await f(a: a, for: b, value))
2424
// CHECK-DUMP: }
2525
// CHECK-DUMP: }
26+
27+
func useOverload() {
28+
f(a: 1, for: "", 2.0) {
29+
print($0)
30+
}
31+
}
32+
}
33+
34+
// CHECK-DUMP: @__swiftmacro_18macro_expand_peers1f1a3for_SSSi_SSSdtYaF20addCompletionHandlerfMp_.swift
35+
// CHECK-DUMP: func f(a: Int, for b: String, _ value: Double, completionHandler: @escaping (String) -> Void) {
36+
// CHECK-DUMP: Task {
37+
// CHECK-DUMP: completionHandler(await f(a: a, for: b, value))
38+
// CHECK-DUMP: }
39+
// CHECK-DUMP: }
40+
41+
@addCompletionHandler
42+
func f(a: Int, for b: String, _ value: Double) async -> String {
43+
return b
44+
}
45+
46+
func useOverload() {
47+
f(a: 1, for: "", 2.0) {
48+
print($0)
49+
}
2650
}
2751

52+
2853
@attached(peer)
2954
macro wrapInType() = #externalMacro(module: "MacroDefinition", type: "WrapInType")
3055

0 commit comments

Comments
 (0)