Skip to content

Commit 939376b

Browse files
committed
[Macros] Enable freestanding macros at module scope in script mode
Eliminate the error message error: global freestanding macros not yet supported in script mode by implementing name lookup, type checking, and code emission for freestanding macros. The key problem here is that, in script mode, it is ambiguous whether a use of a freestanding macro is an expression or a declaration. We parse as an expression (as we do within a function body), which then gets wrapped in a top-level code declaration. Teach various parts of the compiler to look through a top-level code declaration wrapping a macro expansion expression that is for a declaration or code-item macro, e.g., by recording these for global name lookup and treating their expansions as "auxiliary" declarations. Fixes rdar://109699501.
1 parent 6fca08a commit 939376b

File tree

8 files changed

+207
-76
lines changed

8 files changed

+207
-76
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8449,7 +8449,7 @@ class MissingDecl : public Decl {
84498449
/// \c unexpandedMacro contains the macro reference and the base declaration
84508450
/// where the macro expansion applies.
84518451
struct {
8452-
llvm::PointerUnion<MacroExpansionDecl *, CustomAttr *> macroRef;
8452+
llvm::PointerUnion<FreestandingMacroExpansion *, CustomAttr *> macroRef;
84538453
Decl *baseDecl;
84548454
} unexpandedMacro;
84558455

@@ -8473,7 +8473,7 @@ class MissingDecl : public Decl {
84738473

84748474
static MissingDecl *
84758475
forUnexpandedMacro(
8476-
llvm::PointerUnion<MacroExpansionDecl *, CustomAttr *> macroRef,
8476+
llvm::PointerUnion<FreestandingMacroExpansion *, CustomAttr *> macroRef,
84778477
Decl *baseDecl) {
84788478
auto &ctx = baseDecl->getASTContext();
84798479
auto *dc = baseDecl->getDeclContext();

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7244,9 +7244,6 @@ ERROR(literal_type_in_macro_expansion,none,
72447244
ERROR(invalid_macro_introduced_name,none,
72457245
"declaration name %0 is not covered by macro %1",
72467246
(DeclName, DeclName))
7247-
ERROR(global_freestanding_macro_script,none,
7248-
"global freestanding macros not yet supported in script mode",
7249-
())
72507247
ERROR(invalid_macro_role_for_macro_syntax,none,
72517248
"invalid macro role for %{a freestanding|an attached}0 macro",
72527249
(unsigned))

lib/AST/Decl.cpp

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,25 @@ void Decl::visitAuxiliaryDecls(
408408
}
409409

410410
if (visitFreestandingExpanded) {
411-
if (auto *med = dyn_cast<MacroExpansionDecl>(mutableThis)) {
411+
Decl *thisDecl = mutableThis;
412+
413+
// If this is a top-level code decl consisting of a macro expansion
414+
// expression that substituted with a macro expansion declaration, use
415+
// that instead.
416+
if (auto *tlcd = dyn_cast<TopLevelCodeDecl>(thisDecl)) {
417+
if (auto body = tlcd->getBody()) {
418+
if (body->getNumElements() == 1) {
419+
if (auto expr = body->getFirstElement().dyn_cast<Expr *>()) {
420+
if (auto expansion = dyn_cast<MacroExpansionExpr>(expr)) {
421+
if (auto substitute = expansion->getSubstituteDecl())
422+
thisDecl = substitute;
423+
}
424+
}
425+
}
426+
}
427+
}
428+
429+
if (auto *med = dyn_cast<MacroExpansionDecl>(thisDecl)) {
412430
if (auto bufferID = evaluateOrDefault(
413431
ctx.evaluator, ExpandMacroExpansionDeclRequest{med}, {})) {
414432
auto startLoc = sourceMgr.getLocForBufferStart(*bufferID);
@@ -10492,6 +10510,45 @@ bool swift::isMacroSupported(MacroRole role, ASTContext &ctx) {
1049210510
void MissingDecl::forEachMacroExpandedDecl(MacroExpandedDeclCallback callback) {
1049310511
auto macroRef = unexpandedMacro.macroRef;
1049410512
auto *baseDecl = unexpandedMacro.baseDecl;
10513+
10514+
// If the macro itself is a macro expansion expression, it should come with
10515+
// a top-level code declaration that we can use for resolution. For such
10516+
// cases, resolve the macro to determine whether it is a declaration or
10517+
// code-item macro, meaning that it can produce declarations. In such cases,
10518+
// expand the macro and use its substituted declaration (a MacroExpansionDecl)
10519+
// instead.
10520+
if (auto freestanding = macroRef.dyn_cast<FreestandingMacroExpansion *>()) {
10521+
if (auto expr = dyn_cast<MacroExpansionExpr>(freestanding)) {
10522+
bool replacedWithDecl = false;
10523+
if (auto tlcd = dyn_cast_or_null<TopLevelCodeDecl>(baseDecl)) {
10524+
ASTContext &ctx = tlcd->getASTContext();
10525+
if (auto macro = evaluateOrDefault(
10526+
ctx.evaluator,
10527+
ResolveMacroRequest{macroRef, tlcd->getDeclContext()},
10528+
nullptr)) {
10529+
auto macroDecl = cast<MacroDecl>(macro.getDecl());
10530+
auto roles = macroDecl->getMacroRoles();
10531+
if (roles.contains(MacroRole::Declaration) ||
10532+
roles.contains(MacroRole::CodeItem)) {
10533+
(void)evaluateOrDefault(ctx.evaluator,
10534+
ExpandMacroExpansionExprRequest{expr},
10535+
llvm::None);
10536+
if (auto substituted = expr->getSubstituteDecl()) {
10537+
macroRef = substituted;
10538+
baseDecl = substituted;
10539+
replacedWithDecl = true;
10540+
}
10541+
}
10542+
}
10543+
}
10544+
10545+
// If we didn't end up replacing the macro expansion expression with
10546+
// a declaration, we're done.
10547+
if (!replacedWithDecl)
10548+
return;
10549+
}
10550+
}
10551+
1049510552
if (!macroRef || !baseDecl)
1049610553
return;
1049710554
auto *module = getModuleContext();
@@ -10502,9 +10559,12 @@ void MissingDecl::forEachMacroExpandedDecl(MacroExpandedDeclCallback callback) {
1050210559
: auxiliaryDecl->getInnermostDeclContext()->getParentSourceFile();
1050310560
// We only visit auxiliary decls that are macro expansions associated with
1050410561
// this macro reference.
10505-
if (auto *med = macroRef.dyn_cast<MacroExpansionDecl *>()) {
10506-
if (med != sf->getMacroExpansion().dyn_cast<Decl *>())
10507-
return;
10562+
if (auto *med = macroRef.dyn_cast<FreestandingMacroExpansion *>()) {
10563+
auto medAsDecl = dyn_cast<MacroExpansionDecl>(med);
10564+
auto medAsExpr = dyn_cast<MacroExpansionExpr>(med);
10565+
if ((!medAsDecl || medAsDecl != sf->getMacroExpansion().dyn_cast<Decl *>()) &&
10566+
(!medAsExpr || medAsExpr != sf->getMacroExpansion().dyn_cast<Expr *>()))
10567+
return;
1050810568
} else if (auto *attr = macroRef.dyn_cast<CustomAttr *>()) {
1050910569
if (attr != sf->getAttachedMacroAttribute())
1051010570
return;

lib/AST/Module.cpp

Lines changed: 118 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ class swift::SourceLookupCache {
179179
/// Top-level macros that produce arbitrary names.
180180
SmallVector<MissingDecl *, 4> TopLevelArbitraryMacros;
181181

182-
SmallVector<Decl *, 4> MayHaveAuxiliaryDecls;
182+
SmallVector<llvm::PointerUnion<Decl *, MacroExpansionExpr *>, 4>
183+
MayHaveAuxiliaryDecls;
183184
void populateAuxiliaryDeclCache();
184-
185185
SourceLookupCache(ASTContext &ctx);
186186

187187
public:
@@ -238,10 +238,30 @@ SourceLookupCache &SourceFile::getCache() const {
238238
return *Cache;
239239
}
240240

241+
static Expr *getAsExpr(Decl *decl) { return nullptr; }
242+
static Decl *getAsDecl(Decl *decl) { return decl; }
243+
244+
static Expr *getAsExpr(ASTNode node) { return node.dyn_cast<Expr *>(); }
245+
static Decl *getAsDecl(ASTNode node) { return node.dyn_cast<Decl *>(); }
246+
241247
template<typename Range>
242-
void SourceLookupCache::addToUnqualifiedLookupCache(Range decls,
248+
void SourceLookupCache::addToUnqualifiedLookupCache(Range items,
243249
bool onlyOperators) {
244-
for (Decl *D : decls) {
250+
for (auto item : items) {
251+
// In script mode, we'll see macro expansion expressions for freestanding
252+
// macros.
253+
if (Expr *E = getAsExpr(item)) {
254+
if (auto MEE = dyn_cast<MacroExpansionExpr>(E)) {
255+
if (!onlyOperators)
256+
MayHaveAuxiliaryDecls.push_back(MEE);
257+
}
258+
continue;
259+
}
260+
261+
Decl *D = getAsDecl(item);
262+
if (!D)
263+
continue;
264+
245265
if (auto *VD = dyn_cast<ValueDecl>(D)) {
246266
if (onlyOperators ? VD->isOperator() : VD->hasName()) {
247267
// Cache the value under both its compound name and its full name.
@@ -280,6 +300,10 @@ void SourceLookupCache::addToUnqualifiedLookupCache(Range decls,
280300
else if (auto *MED = dyn_cast<MacroExpansionDecl>(D)) {
281301
if (!onlyOperators)
282302
MayHaveAuxiliaryDecls.push_back(MED);
303+
} else if (auto TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
304+
if (auto body = TLCD->getBody()){
305+
addToUnqualifiedLookupCache(body->getElements(), onlyOperators);
306+
}
283307
}
284308
}
285309
}
@@ -335,75 +359,113 @@ void SourceLookupCache::addToMemberCache(Range decls) {
335359
}
336360

337361
void SourceLookupCache::populateAuxiliaryDeclCache() {
338-
using MacroRef = llvm::PointerUnion<MacroExpansionDecl *, CustomAttr *>;
339-
for (auto *decl : MayHaveAuxiliaryDecls) {
362+
using MacroRef = llvm::PointerUnion<FreestandingMacroExpansion *, CustomAttr *>;
363+
for (auto item : MayHaveAuxiliaryDecls) {
364+
TopLevelCodeDecl *topLevelCodeDecl = nullptr;
365+
340366
// Gather macro-introduced peer names.
341367
llvm::SmallDenseMap<MacroRef, llvm::SmallVector<DeclName, 2>>
342368
introducedNames;
343369

344-
// This code deliberately avoids `forEachAttachedMacro`, because it
345-
// will perform overload resolution and possibly invoke unqualified
346-
// lookup for macro arguments, which will recursively populate the
347-
// auxiliary decl cache and cause request cycles.
348-
//
349-
// We do not need a fully resolved macro until expansion. Instead, we
350-
// conservatively consider peer names for all macro declarations with a
351-
// custom attribute name. Unqualified lookup for that name will later
352-
// invoke expansion of the macro, and will yield no results if the resolved
353-
// macro does not produce the requested name, so the only impact is possibly
354-
// expanding earlier than needed / unnecessarily looking in the top-level
355-
// auxiliary decl cache.
356-
for (auto attrConst : decl->getAttrs().getAttributes<CustomAttr>()) {
357-
auto *attr = const_cast<CustomAttr *>(attrConst);
358-
UnresolvedMacroReference macroRef(attr);
359-
bool introducesArbitraryNames = false;
360-
namelookup::forEachPotentialResolvedMacro(
361-
decl->getDeclContext()->getModuleScopeContext(),
362-
macroRef.getMacroName(), MacroRole::Peer,
363-
[&](MacroDecl *macro, const MacroRoleAttr *roleAttr) {
364-
// First check for arbitrary names.
365-
if (roleAttr->hasNameKind(MacroIntroducedDeclNameKind::Arbitrary)) {
366-
introducesArbitraryNames = true;
367-
}
370+
/// Introduce names for a freestanding macro.
371+
auto introduceNamesForFreestandingMacro =
372+
[&](FreestandingMacroExpansion *macroRef, Decl *decl, MacroRole role) {
373+
bool introducesArbitraryNames = false;
374+
namelookup::forEachPotentialResolvedMacro(
375+
decl->getDeclContext()->getModuleScopeContext(),
376+
macroRef->getMacroName(), role,
377+
[&](MacroDecl *macro, const MacroRoleAttr *roleAttr) {
378+
// First check for arbitrary names.
379+
if (roleAttr->hasNameKind(MacroIntroducedDeclNameKind::Arbitrary)) {
380+
introducesArbitraryNames = true;
381+
}
368382

369-
macro->getIntroducedNames(MacroRole::Peer,
370-
dyn_cast<ValueDecl>(decl),
371-
introducedNames[attr]);
372-
});
383+
macro->getIntroducedNames(role,
384+
/*attachedTo*/ nullptr,
385+
introducedNames[macroRef]);
386+
});
373387

374-
// Record this macro where appropriate.
375-
if (introducesArbitraryNames)
376-
TopLevelArbitraryMacros.push_back(MissingDecl::forUnexpandedMacro(attr, decl));
388+
return introducesArbitraryNames;
389+
};
390+
391+
// Handle macro expansion expressions, which show up in when we have
392+
// freestanding macros in "script" mode.
393+
if (auto expr = item.dyn_cast<MacroExpansionExpr *>()) {
394+
topLevelCodeDecl = dyn_cast<TopLevelCodeDecl>(expr->getDeclContext());
395+
if (topLevelCodeDecl) {
396+
bool introducesArbitraryNames = false;
397+
if (introduceNamesForFreestandingMacro(
398+
expr, topLevelCodeDecl, MacroRole::Declaration))
399+
introducesArbitraryNames = true;
400+
401+
if (introduceNamesForFreestandingMacro(
402+
expr, topLevelCodeDecl, MacroRole::CodeItem))
403+
introducesArbitraryNames = true;
404+
405+
// Record this macro if it introduces arbitrary names.
406+
if (introducesArbitraryNames) {
407+
TopLevelArbitraryMacros.push_back(
408+
MissingDecl::forUnexpandedMacro(expr, topLevelCodeDecl));
409+
}
410+
}
377411
}
378412

379-
if (auto *med = dyn_cast<MacroExpansionDecl>(decl)) {
380-
UnresolvedMacroReference macroRef(med);
381-
bool introducesArbitraryNames = false;
382-
namelookup::forEachPotentialResolvedMacro(
383-
decl->getDeclContext()->getModuleScopeContext(),
384-
macroRef.getMacroName(), MacroRole::Declaration,
385-
[&](MacroDecl *macro, const MacroRoleAttr *roleAttr) {
386-
// First check for arbitrary names.
387-
if (roleAttr->hasNameKind(MacroIntroducedDeclNameKind::Arbitrary)) {
388-
introducesArbitraryNames = true;
389-
}
413+
auto *decl = item.dyn_cast<Decl *>();
414+
if (decl) {
415+
// This code deliberately avoids `forEachAttachedMacro`, because it
416+
// will perform overload resolution and possibly invoke unqualified
417+
// lookup for macro arguments, which will recursively populate the
418+
// auxiliary decl cache and cause request cycles.
419+
//
420+
// We do not need a fully resolved macro until expansion. Instead, we
421+
// conservatively consider peer names for all macro declarations with a
422+
// custom attribute name. Unqualified lookup for that name will later
423+
// invoke expansion of the macro, and will yield no results if the resolved
424+
// macro does not produce the requested name, so the only impact is possibly
425+
// expanding earlier than needed / unnecessarily looking in the top-level
426+
// auxiliary decl cache.
427+
for (auto attrConst : decl->getAttrs().getAttributes<CustomAttr>()) {
428+
auto *attr = const_cast<CustomAttr *>(attrConst);
429+
UnresolvedMacroReference macroRef(attr);
430+
bool introducesArbitraryNames = false;
431+
namelookup::forEachPotentialResolvedMacro(
432+
decl->getDeclContext()->getModuleScopeContext(),
433+
macroRef.getMacroName(), MacroRole::Peer,
434+
[&](MacroDecl *macro, const MacroRoleAttr *roleAttr) {
435+
// First check for arbitrary names.
436+
if (roleAttr->hasNameKind(
437+
MacroIntroducedDeclNameKind::Arbitrary)) {
438+
introducesArbitraryNames = true;
439+
}
440+
441+
macro->getIntroducedNames(MacroRole::Peer,
442+
dyn_cast<ValueDecl>(decl),
443+
introducedNames[attr]);
444+
});
445+
446+
// Record this macro where appropriate.
447+
if (introducesArbitraryNames)
448+
TopLevelArbitraryMacros.push_back(
449+
MissingDecl::forUnexpandedMacro(attr, decl));
450+
}
451+
}
390452

391-
macro->getIntroducedNames(MacroRole::Declaration,
392-
/*attachedTo*/ nullptr,
393-
introducedNames[med]);
394-
});
453+
if (auto *med = dyn_cast_or_null<MacroExpansionDecl>(decl)) {
454+
bool introducesArbitraryNames =
455+
introduceNamesForFreestandingMacro(med, decl, MacroRole::Declaration);
395456

396-
// Record this macro where appropriate.
457+
// Note whether this macro produces arbitrary names.
397458
if (introducesArbitraryNames)
398459
TopLevelArbitraryMacros.push_back(MissingDecl::forUnexpandedMacro(med, decl));
399460
}
400461

401462
// Add macro-introduced names to the top-level auxiliary decl cache as
402463
// unexpanded decls represented by a MissingDecl.
464+
auto anchorDecl = decl ? decl : topLevelCodeDecl;
403465
for (auto macroNames : introducedNames) {
404466
auto macroRef = macroNames.getFirst();
405467
for (auto name : macroNames.getSecond()) {
406-
auto *placeholder = MissingDecl::forUnexpandedMacro(macroRef, decl);
468+
auto *placeholder = MissingDecl::forUnexpandedMacro(macroRef, anchorDecl);
407469
name.addToLookupTable(TopLevelAuxiliaryDecls, placeholder);
408470
}
409471
}
@@ -421,7 +483,7 @@ SourceLookupCache::SourceLookupCache(const SourceFile &SF)
421483
{
422484
FrontendStatsTracer tracer(SF.getASTContext().Stats,
423485
"source-file-populate-cache");
424-
addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false);
486+
addToUnqualifiedLookupCache(SF.getTopLevelItems(), false);
425487
addToUnqualifiedLookupCache(SF.getHoistedDecls(), false);
426488
}
427489

@@ -432,7 +494,7 @@ SourceLookupCache::SourceLookupCache(const ModuleDecl &M)
432494
"module-populate-cache");
433495
for (const FileUnit *file : M.getFiles()) {
434496
auto *SF = cast<SourceFile>(file);
435-
addToUnqualifiedLookupCache(SF->getTopLevelDecls(), false);
497+
addToUnqualifiedLookupCache(SF->getTopLevelItems(), false);
436498
addToUnqualifiedLookupCache(SF->getHoistedDecls(), false);
437499

438500
if (auto *SFU = file->getSynthesizedFile()) {

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3926,11 +3926,5 @@ ExpandMacroExpansionDeclRequest::evaluate(Evaluator &evaluator,
39263926
!roles.contains(MacroRole::CodeItem))
39273927
return llvm::None;
39283928

3929-
// For now, restrict global freestanding macros in script mode.
3930-
if (dc->isModuleScopeContext() &&
3931-
dc->getParentSourceFile()->isScriptMode()) {
3932-
MED->diagnose(diag::global_freestanding_macro_script);
3933-
}
3934-
39353929
return expandFreestandingMacro(MED);
39363930
}

lib/Sema/TypeCheckMacros.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,7 @@ ExpandMacroExpansionExprRequest::evaluate(Evaluator &evaluator,
469469
else if (macro->getMacroRoles().contains(MacroRole::Declaration) ||
470470
macro->getMacroRoles().contains(MacroRole::CodeItem)) {
471471
if (!mee->getSubstituteDecl()) {
472-
auto *med = mee->createSubstituteDecl();
473-
TypeChecker::typeCheckDecl(med);
472+
(void)mee->createSubstituteDecl();
474473
}
475474
// Return the expanded buffer ID.
476475
return evaluateOrDefault(

0 commit comments

Comments
 (0)