Skip to content

Commit cb20be1

Browse files
committed
[Macros] Code item macros
Add support for declaring and expanding code item macros. Add experimental feature flag CodeItemMacros.
1 parent 86ad0dd commit cb20be1

24 files changed

+217
-18
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,8 +2049,8 @@ ERROR(macro_role_attr_expected_kind,PointsToFirstBadToken,
20492049
"expected %select{a freestanding|an attached}0 macro role such as "
20502050
"%select{'expression'|'accessor'}0", (bool))
20512051
ERROR(macro_role_syntax_mismatch,PointsToFirstBadToken,
2052-
"expected %select{a freestanding|an attached}0 macro cannot have "
2053-
"the %1 role", (bool, Identifier))
2052+
"%select{a freestanding|an attached}0 macro cannot have the %1 role",
2053+
(bool, Identifier))
20542054
ERROR(macro_attribute_unknown_label,PointsToFirstBadToken,
20552055
"@%select{freestanding|attached}0 has no argument with label %1",
20562056
(bool, Identifier))

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7025,6 +7025,11 @@ ERROR(invalid_macro_introduced_name,none,
70257025
ERROR(global_freestanding_macro_script,none,
70267026
"global freestanding macros not yet supported in script mode",
70277027
())
7028+
ERROR(invalid_macro_role_for_macro_syntax,none,
7029+
"invalid macro role for %{a freestanding|an attached}0 macro",
7030+
(unsigned))
7031+
ERROR(macro_cannot_introduce_names,none,
7032+
"'%0' macros are not allowed to introduce names", (StringRef))
70287033

70297034
//------------------------------------------------------------------------------
70307035
// MARK: Move Only Errors

include/swift/AST/MacroDeclaration.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ enum class MacroRole: uint32_t {
5555
/// An attached macro that adds conformances to the declaration the
5656
/// macro is attached to.
5757
Conformance = 0x40,
58+
/// A freestanding macro that expands to expressions, statements and
59+
/// declarations in a code block.
60+
CodeItem = 0x80,
5861
};
5962

6063
/// The contexts in which a particular macro declaration can be used.

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ EXPERIMENTAL_FEATURE(VariadicGenerics, false)
112112
EXPERIMENTAL_FEATURE(NamedOpaqueTypes, false)
113113
EXPERIMENTAL_FEATURE(FlowSensitiveConcurrencyCaptures, false)
114114
EXPERIMENTAL_FEATURE(FreestandingMacros, true)
115+
EXPERIMENTAL_FEATURE(CodeItemMacros, true)
115116

116117
// FIXME: MoveOnlyClasses is not intended to be in production,
117118
// but our tests currently rely on it, and we want to run those

lib/AST/ASTMangler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3965,6 +3965,7 @@ void ASTMangler::appendMacroExpansionOperator(
39653965
switch (role) {
39663966
case MacroRole::Expression:
39673967
case MacroRole::Declaration:
3968+
case MacroRole::CodeItem:
39683969
appendOperator("fMf", Index(discriminator));
39693970
break;
39703971

lib/AST/ASTPrinter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2838,6 +2838,14 @@ static bool usesFeatureFreestandingMacros(Decl *decl) {
28382838
return macro->getMacroRoles().contains(MacroRole::Declaration);
28392839
}
28402840

2841+
static bool usesFeatureCodeItemMacros(Decl *decl) {
2842+
auto macro = dyn_cast<MacroDecl>(decl);
2843+
if (!macro)
2844+
return false;
2845+
2846+
return macro->getMacroRoles().contains(MacroRole::CodeItem);
2847+
}
2848+
28412849
static bool usesFeatureAttachedMacros(Decl *decl) {
28422850
auto macro = dyn_cast<MacroDecl>(decl);
28432851
if (!macro)

lib/AST/ASTScopeCreation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
260260
switch (*macroRole) {
261261
case MacroRole::Expression:
262262
case MacroRole::Declaration:
263+
case MacroRole::CodeItem:
263264
case MacroRole::Accessor:
264265
case MacroRole::MemberAttribute:
265266
case MacroRole::Peer:

lib/AST/ASTWalker.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,9 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
442442
}
443443

444444
bool visitMacroExpansionDecl(MacroExpansionDecl *MED) {
445+
#ifndef NDEBUG
446+
PrettyStackTraceDecl debugStack("walking into", MED);
447+
#endif
445448
bool shouldWalkArguments, shouldWalkExpansion;
446449
std::tie(shouldWalkArguments, shouldWalkExpansion) =
447450
Walker.shouldWalkMacroArgumentsAndExpansion();
@@ -1341,6 +1344,10 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
13411344
std::tie(shouldWalkArguments, shouldWalkExpansion) =
13421345
Walker.shouldWalkMacroArgumentsAndExpansion();
13431346

1347+
if (auto *substituteDecl = E->getSubstituteDecl())
1348+
if (doIt(substituteDecl))
1349+
return nullptr;
1350+
13441351
if (shouldWalkArguments && E->getArgs()) {
13451352
ArgumentList *args = doIt(E->getArgs());
13461353
if (!args) return nullptr;
@@ -1355,6 +1362,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
13551362
}
13561363
E->setRewritten(rewritten);
13571364
}
1365+
13581366
return E;
13591367
}
13601368

lib/AST/Decl.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10055,6 +10055,9 @@ StringRef swift::getMacroRoleString(MacroRole role) {
1005510055

1005610056
case MacroRole::Conformance:
1005710057
return "conformance";
10058+
10059+
case MacroRole::CodeItem:
10060+
return "codeItem";
1005810061
}
1005910062
}
1006010063

@@ -10096,7 +10099,8 @@ StringRef swift::getMacroIntroducedDeclNameString(
1009610099
static MacroRoles freestandingMacroRoles =
1009710100
(MacroRoles() |
1009810101
MacroRole::Expression |
10099-
MacroRole::Declaration);
10102+
MacroRole::Declaration |
10103+
MacroRole::CodeItem);
1010010104
static MacroRoles attachedMacroRoles = (MacroRoles() |
1010110105
MacroRole::Accessor |
1010210106
MacroRole::MemberAttribute |
@@ -10302,6 +10306,7 @@ void MacroDecl::getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
1030210306
case MacroRole::Declaration:
1030310307
case MacroRole::Member:
1030410308
case MacroRole::Peer:
10309+
case MacroRole::CodeItem:
1030510310
names.push_back(MacroDecl::getUniqueNamePlaceholder(getASTContext()));
1030610311
break;
1030710312

lib/ASTGen/Sources/ASTGen/Macros.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,29 @@ func expandFreestandingMacroInProcess(
602602
evaluatedSyntax = Syntax(CodeBlockItemListSyntax(
603603
decls.map { CodeBlockItemSyntax(item: .decl($0)) }))
604604

605+
case let codeItemMacro as CodeItemMacro.Type:
606+
guard let parentExpansion = macroSyntax.asProtocol(
607+
FreestandingMacroExpansionSyntax.self
608+
) else {
609+
print("not on a macro expansion decl: \(macroSyntax.recursiveDescription)")
610+
return nil
611+
}
612+
macroName = parentExpansion.macro.text
613+
614+
func expandDeclarationMacro<Node: FreestandingMacroExpansionSyntax>(
615+
_ node: Node
616+
) throws -> [CodeBlockItemSyntax] {
617+
return try codeItemMacro.expansion(
618+
of: sourceManager.detach(
619+
node,
620+
foldingWith: OperatorTable.standardOperators
621+
),
622+
in: context
623+
)
624+
}
625+
let items = try _openExistential(parentExpansion, do: expandDeclarationMacro)
626+
evaluatedSyntax = Syntax(CodeBlockItemListSyntax(items))
627+
605628
default:
606629
print("not an expression macro or a declaration macro")
607630
return nil

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,7 @@ static Optional<MacroRole> getMacroRole(
21992199
auto role = llvm::StringSwitch<Optional<MacroRole>>(roleName->str())
22002200
.Case("declaration", MacroRole::Declaration)
22012201
.Case("expression", MacroRole::Expression)
2202+
.Case("codeItem", MacroRole::CodeItem)
22022203
.Case("accessor", MacroRole::Accessor)
22032204
.Case("memberAttribute", MacroRole::MemberAttribute)
22042205
.Case("member", MacroRole::Member)

lib/SILGen/SILGenExpr.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6169,10 +6169,18 @@ RValue RValueEmitter::visitMacroExpansionExpr(MacroExpansionExpr *E,
61696169
MacroScope scope(SGF, CleanupLocation(rewritten), E, name.str(),
61706170
E->getMacroRef().getDecl());
61716171
return visit(rewritten, C);
6172-
} else {
6173-
assert(E->getSubstituteDecl());
6172+
}
6173+
else if (auto *MED = E->getSubstituteDecl()) {
6174+
Mangle::ASTMangler mangler;
6175+
MED->forEachExpandedExprOrStmt([&](ASTNode node) {
6176+
if (auto *expr = node.dyn_cast<Expr *>())
6177+
visit(expr, C);
6178+
else if (auto *stmt = node.dyn_cast<Stmt *>())
6179+
SGF.emitStmt(stmt);
6180+
});
61746181
return RValue();
61756182
}
6183+
return RValue();
61766184
}
61776185

61786186
RValue SILGenFunction::emitRValue(Expr *E, SGFContext C) {

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5394,7 +5394,9 @@ namespace {
53945394
}
53955395
}
53965396
// For a non-expression macro, expand it as a declaration.
5397-
else if (macro->getMacroRoles().contains(MacroRole::Declaration)) {
5397+
else if (macro->getMacroRoles().contains(MacroRole::Declaration) ||
5398+
(macro->getMacroRoles().contains(MacroRole::CodeItem) &&
5399+
cs.getASTContext().LangOpts.hasFeature(Feature::CodeItemMacros))) {
53985400
auto &ctx = cs.getASTContext();
53995401
if (!E->getSubstituteDecl()) {
54005402
auto *med = E->createSubstituteDecl();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
160160
IGNORED_ATTR(Preconcurrency)
161161
IGNORED_ATTR(BackDeployed)
162162
IGNORED_ATTR(Documentation)
163-
IGNORED_ATTR(MacroRole)
164163
IGNORED_ATTR(LexicalLifetimes)
165164
#undef IGNORED_ATTR
166165

@@ -342,6 +341,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
342341
void visitSendableAttr(SendableAttr *attr);
343342

344343
void visitRuntimeMetadataAttr(RuntimeMetadataAttr *attr);
344+
345+
void visitMacroRoleAttr(MacroRoleAttr *attr);
345346
};
346347

347348
} // end anonymous namespace
@@ -6959,6 +6960,59 @@ void AttributeChecker::visitRuntimeMetadataAttr(RuntimeMetadataAttr *attr) {
69596960
}
69606961
}
69616962

6963+
void AttributeChecker::visitMacroRoleAttr(MacroRoleAttr *attr) {
6964+
switch (attr->getMacroSyntax()) {
6965+
case MacroSyntax::Freestanding: {
6966+
switch (attr->getMacroRole()) {
6967+
case MacroRole::Expression:
6968+
if (!attr->getNames().empty())
6969+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
6970+
getMacroRoleString(attr->getMacroRole()));
6971+
break;
6972+
case MacroRole::Declaration:
6973+
// TODO: Check names
6974+
break;
6975+
case MacroRole::CodeItem:
6976+
if (!attr->getNames().empty())
6977+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
6978+
getMacroRoleString(attr->getMacroRole()));
6979+
break;
6980+
default:
6981+
diagnoseAndRemoveAttr(attr, diag::invalid_macro_role_for_macro_syntax,
6982+
/*freestanding*/0);
6983+
break;
6984+
}
6985+
break;
6986+
}
6987+
case MacroSyntax::Attached: {
6988+
switch (attr->getMacroRole()) {
6989+
case MacroRole::Accessor:
6990+
// TODO: Check property observer names?
6991+
break;
6992+
case MacroRole::MemberAttribute:
6993+
if (!attr->getNames().empty())
6994+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
6995+
getMacroRoleString(attr->getMacroRole()));
6996+
break;
6997+
case MacroRole::Member:
6998+
break;
6999+
case MacroRole::Peer:
7000+
break;
7001+
case MacroRole::Conformance:
7002+
if (!attr->getNames().empty())
7003+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
7004+
getMacroRoleString(attr->getMacroRole()));
7005+
break;
7006+
default:
7007+
diagnoseAndRemoveAttr(attr, diag::invalid_macro_role_for_macro_syntax,
7008+
/*attached*/1);
7009+
break;
7010+
}
7011+
break;
7012+
}
7013+
}
7014+
}
7015+
69627016
namespace {
69637017

69647018
class ClosureAttributeChecker

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,7 +2037,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20372037
void visitMacroExpansionDecl(MacroExpansionDecl *MED) {
20382038
// Assign a discriminator.
20392039
(void)MED->getDiscriminator();
2040-
// Expansion already visited as auxiliary decls.
2040+
// Decls in expansion already visited as auxiliary decls.
2041+
MED->forEachExpandedExprOrStmt([&](ASTNode node) {
2042+
TypeChecker::typeCheckASTNode(node, MED->getDeclContext());
2043+
});
20412044
}
20422045

20432046
void visitBoundVariable(VarDecl *VD) {
@@ -3789,12 +3792,11 @@ ExpandMacroExpansionDeclRequest::evaluate(Evaluator &evaluator,
37893792
// If it's not a declaration macro or a code item macro, it must have been
37903793
// parsed as an expression macro, and this decl is just its substitute decl.
37913794
// So there's no thing to be done here.
3792-
if (!roles.contains(MacroRole::Declaration))
3795+
if (!roles.contains(MacroRole::Declaration) &&
3796+
!(roles.contains(MacroRole::CodeItem) &&
3797+
ctx.LangOpts.hasFeature(Feature::CodeItemMacros)))
37933798
return None;
37943799

3795-
// Otherwise, we treat it as a declaration macro.
3796-
assert(roles.contains(MacroRole::Declaration));
3797-
37983800
// For now, restrict global freestanding macros in script mode.
37993801
if (dc->isModuleScopeContext() &&
38003802
dc->getParentSourceFile()->isScriptMode()) {

lib/Sema/TypeCheckEffects.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ class EffectsHandlingWalker : public ASTWalker {
413413
} else if (auto patternBinding = dyn_cast<PatternBindingDecl>(D)) {
414414
if (patternBinding->isAsyncLet())
415415
recurse = asImpl().checkAsyncLet(patternBinding);
416+
} else if (auto macroExpansionDecl = dyn_cast<MacroExpansionDecl>(D)) {
417+
recurse = ShouldRecurse;
416418
} else {
417419
recurse = ShouldNotRecurse;
418420
}
@@ -444,6 +446,8 @@ class EffectsHandlingWalker : public ASTWalker {
444446
recurse = asImpl().checkDeclRef(declRef);
445447
} else if (auto interpolated = dyn_cast<InterpolatedStringLiteralExpr>(E)) {
446448
recurse = asImpl().checkInterpolatedStringLiteral(interpolated);
449+
} else if (auto macroExpansionExpr = dyn_cast<MacroExpansionExpr>(E)) {
450+
recurse = ShouldRecurse;
447451
}
448452
// Error handling validation (via checkTopLevelEffects) happens after
449453
// type checking. If an unchecked expression is still around, the code was

lib/Sema/TypeCheckMacros.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,10 +885,13 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) {
885885
NullTerminatedStringRef evaluatedSource;
886886

887887
MacroDecl *macro = cast<MacroDecl>(med->getMacroRef().getDecl());
888-
assert(macro->getMacroRoles().contains(MacroRole::Declaration));
888+
auto macroRoles = macro->getMacroRoles();
889+
assert(macroRoles.contains(MacroRole::Declaration) ||
890+
macroRoles.contains(MacroRole::CodeItem));
889891

890892
if (isFromExpansionOfMacro(sourceFile, macro, MacroRole::Expression) ||
891-
isFromExpansionOfMacro(sourceFile, macro, MacroRole::Declaration)) {
893+
isFromExpansionOfMacro(sourceFile, macro, MacroRole::Declaration) ||
894+
isFromExpansionOfMacro(sourceFile, macro, MacroRole::CodeItem)) {
892895
med->diagnose(diag::macro_recursive, macro->getName());
893896
return None;
894897
}
@@ -1284,6 +1287,7 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr,
12841287

12851288
case MacroRole::Expression:
12861289
case MacroRole::Declaration:
1290+
case MacroRole::CodeItem:
12871291
llvm_unreachable("freestanding macro in attached macro evaluation");
12881292
}
12891293

lib/Serialization/Deserialization.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,6 +2670,7 @@ getActualMacroRole(uint8_t context) {
26702670
CASE(Member)
26712671
CASE(Peer)
26722672
CASE(Conformance)
2673+
CASE(CodeItem)
26732674
#undef CASE
26742675
}
26752676
return None;

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ enum class MacroRole : uint8_t {
619619
Member,
620620
Peer,
621621
Conformance,
622+
CodeItem,
622623
};
623624
using MacroRoleField = BCFixed<3>;
624625

lib/Serialization/Serialization.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,6 +2239,7 @@ static uint8_t getRawStableMacroRole(swift::MacroRole context) {
22392239
CASE(Member)
22402240
CASE(Peer)
22412241
CASE(Conformance)
2242+
CASE(CodeItem)
22422243
}
22432244
#undef CASE
22442245
llvm_unreachable("bad result declaration macro kind");

0 commit comments

Comments
 (0)