Skip to content

Commit b02e458

Browse files
authored
Merge pull request #64065 from hborla/global-peer-macro-lookup
[Macros] Implement unqualified lookup for global macro-expanded peer declarations.
2 parents 4f034f0 + 57cca85 commit b02e458

File tree

4 files changed

+227
-6
lines changed

4 files changed

+227
-6
lines changed

include/swift/AST/Decl.h

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8306,10 +8306,20 @@ class PostfixOperatorDecl : public OperatorDecl {
83068306
}
83078307
};
83088308

8309-
/// Represents a missing declaration in the source code. This
8310-
/// is used for parser recovery, e.g. when parsing a floating
8311-
/// attribute list.
8312-
class MissingDecl: public Decl {
8309+
/// Represents a missing declaration in the source code.
8310+
///
8311+
/// This is used for parser recovery, e.g. when parsing a floating
8312+
/// attribute list, and to represent placeholders for unexpanded
8313+
/// declarations generated by macros.
8314+
class MissingDecl : public Decl {
8315+
/// If this missing decl represents an unexpanded peer generated
8316+
/// by a macro, \c unexpandedPeer contains the macro custom attribute
8317+
/// and the declaration the macro is attached to.
8318+
struct {
8319+
CustomAttr *macroAttr = nullptr;
8320+
ValueDecl *attachedTo = nullptr;
8321+
} unexpandedPeer;
8322+
83138323
/// The location that the decl would be if it wasn't missing.
83148324
SourceLoc Loc;
83158325

@@ -8328,6 +8338,21 @@ class MissingDecl: public Decl {
83288338

83298339
SourceRange getSourceRange() const { return SourceRange(Loc); }
83308340

8341+
static MissingDecl *
8342+
forUnexpandedPeer(CustomAttr *macro, ValueDecl *attachedTo) {
8343+
auto &ctx = attachedTo->getASTContext();
8344+
auto *dc = attachedTo->getDeclContext();
8345+
auto *missing = new (ctx) MissingDecl(dc, SourceLoc());
8346+
8347+
missing->unexpandedPeer.macroAttr = macro;
8348+
missing->unexpandedPeer.attachedTo = attachedTo;
8349+
8350+
return missing;
8351+
}
8352+
8353+
using ExpandedPeerCallback = llvm::function_ref<void(ValueDecl *)>;
8354+
void forEachExpandedPeer(ExpandedPeerCallback callback);
8355+
83318356
static bool classof(const Decl *D) {
83328357
return D->getKind() == DeclKind::Missing;
83338358
}
@@ -8433,6 +8458,11 @@ class MacroDecl : public GenericContext, public ValueDecl {
84338458
/// Retrieve the attribute that declared the given macro role.
84348459
const MacroRoleAttr *getMacroRoleAttr(MacroRole role) const;
84358460

8461+
/// Populate the \c names vector with the decl names introduced
8462+
/// by a given role of this macro.
8463+
void getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
8464+
SmallVectorImpl<DeclName> &names) const;
8465+
84368466
/// Retrieve the definition of this macro.
84378467
MacroDefinition getDefinition() const;
84388468

lib/AST/Decl.cpp

Lines changed: 87 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,71 @@ 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+
{
10085+
llvm::raw_string_ostream out(prefixedName);
10086+
out << expandedName.getIdentifier();
10087+
out << baseName.getIdentifier();
10088+
}
10089+
10090+
Identifier nameId = ctx.getIdentifier(prefixedName);
10091+
names.push_back(DeclName(nameId));
10092+
break;
10093+
}
10094+
10095+
case MacroIntroducedDeclNameKind::Suffixed: {
10096+
if (!attachedTo)
10097+
break;
10098+
10099+
auto baseName = attachedTo->getBaseName();
10100+
std::string suffixedName;
10101+
{
10102+
llvm::raw_string_ostream out(suffixedName);
10103+
out << baseName.getIdentifier();
10104+
out << expandedName.getIdentifier();
10105+
}
10106+
10107+
Identifier nameId = ctx.getIdentifier(suffixedName);
10108+
names.push_back(DeclName(nameId));
10109+
break;
10110+
}
10111+
10112+
case MacroIntroducedDeclNameKind::Arbitrary:
10113+
// FIXME: Indicate that the macro covers arbitrary names.
10114+
break;
10115+
}
10116+
}
1003110117
}
1003210118

1003310119
MacroDefinition MacroDecl::getDefinition() const {

lib/AST/Module.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ class swift::SourceLookupCache {
170170
void addToUnqualifiedLookupCache(Range decls, bool onlyOperators);
171171
template<typename Range>
172172
void addToMemberCache(Range decls);
173+
174+
using MacroDeclMap = llvm::DenseMap<Identifier, TinyPtrVector<MacroDecl *>>;
175+
MacroDeclMap MacroDecls;
176+
177+
using AuxiliaryDeclMap = llvm::DenseMap<DeclName, TinyPtrVector<MissingDecl *>>;
178+
AuxiliaryDeclMap TopLevelAuxiliaryDecls;
179+
SmallVector<ValueDecl *, 4> MayHaveAuxiliaryDecls;
180+
void populateAuxiliaryDeclCache();
181+
173182
public:
174183
SourceLookupCache(const SourceFile &SF);
175184
SourceLookupCache(const ModuleDecl &Mod);
@@ -234,6 +243,10 @@ void SourceLookupCache::addToUnqualifiedLookupCache(Range decls,
234243
if (onlyOperators ? VD->isOperator() : VD->hasName()) {
235244
// Cache the value under both its compound name and its full name.
236245
TopLevelValues.add(VD);
246+
247+
if (VD->getAttrs().hasAttribute<CustomAttr>()) {
248+
MayHaveAuxiliaryDecls.push_back(VD);
249+
}
237250
}
238251
}
239252

@@ -256,6 +269,9 @@ void SourceLookupCache::addToUnqualifiedLookupCache(Range decls,
256269

257270
if (auto *PG = dyn_cast<PrecedenceGroupDecl>(D))
258271
PrecedenceGroups[PG->getName()].push_back(PG);
272+
273+
if (auto *macro = dyn_cast<MacroDecl>(D))
274+
MacroDecls[macro->getBaseIdentifier()].push_back(macro);
259275
}
260276
}
261277

@@ -309,6 +325,53 @@ void SourceLookupCache::addToMemberCache(Range decls) {
309325
}
310326
}
311327

328+
void SourceLookupCache::populateAuxiliaryDeclCache() {
329+
for (auto *decl : MayHaveAuxiliaryDecls) {
330+
// Gather macro-introduced peer names.
331+
llvm::SmallDenseMap<CustomAttr *, llvm::SmallVector<DeclName, 2>> introducedNames;
332+
333+
// This code deliberately avoids `forEachAttachedMacro`, because it
334+
// will perform overload resolution and possibly invoke unqualified
335+
// lookup for macro arguments, which will recursively populate the
336+
// auxiliary decl cache and cause request cycles.
337+
//
338+
// We do not need a fully resolved macro until expansion. Instead, we
339+
// conservatively consider peer names for all macro declarations with a
340+
// custom attribute name. Unqualified lookup for that name will later
341+
// invoke expansion of the macro, and will yield no results if the resolved
342+
// macro does not produce the requested name, so the only impact is possibly
343+
// expanding earlier than needed / unnecessarily looking in the top-level
344+
// auxiliary decl cache.
345+
for (auto attrConst : decl->getSemanticAttrs().getAttributes<CustomAttr>()) {
346+
auto *attr = const_cast<CustomAttr *>(attrConst);
347+
UnresolvedMacroReference macroRef(attr);
348+
auto macroName = macroRef.getMacroName().getBaseIdentifier();
349+
350+
auto found = MacroDecls.find(macroName);
351+
if (found == MacroDecls.end())
352+
continue;
353+
354+
for (const auto *macro : found->second) {
355+
macro->getIntroducedNames(MacroRole::Peer,
356+
decl, introducedNames[attr]);
357+
}
358+
}
359+
360+
// Add macro-introduced names to the top-level auxiliary decl cache as
361+
// unexpanded peer decls represented by a MissingDecl.
362+
for (auto macroNames : introducedNames) {
363+
auto *macroAttr = macroNames.getFirst();
364+
for (auto name : macroNames.getSecond()) {
365+
auto *placeholder =
366+
MissingDecl::forUnexpandedPeer(macroAttr, decl);
367+
name.addToLookupTable(TopLevelAuxiliaryDecls, placeholder);
368+
}
369+
}
370+
}
371+
372+
MayHaveAuxiliaryDecls.clear();
373+
}
374+
312375
/// Populate our cache on the first name lookup.
313376
SourceLookupCache::SourceLookupCache(const SourceFile &SF) {
314377
FrontendStatsTracer tracer(SF.getASTContext().Stats,
@@ -339,6 +402,23 @@ void SourceLookupCache::lookupValue(DeclName Name, NLKind LookupKind,
339402
Result.reserve(I->second.size());
340403
for (ValueDecl *Elt : I->second)
341404
Result.push_back(Elt);
405+
406+
// Add top-level auxiliary decls to the result.
407+
//
408+
// FIXME: We need to not consider auxiliary decls if we're doing lookup
409+
// from inside a macro argument at module scope.
410+
populateAuxiliaryDeclCache();
411+
auto auxDecls = TopLevelAuxiliaryDecls.find(Name);
412+
if (auxDecls == TopLevelAuxiliaryDecls.end())
413+
return;
414+
415+
for (auto *unexpandedDecl : auxDecls->second) {
416+
// Add expanded peers to the result.
417+
unexpandedDecl->forEachExpandedPeer(
418+
[&](ValueDecl *expandedPeer) {
419+
Result.push_back(expandedPeer);
420+
});
421+
}
342422
}
343423

344424
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)