Skip to content

Commit 9f9e709

Browse files
authored
Merge pull request #64895 from rxwei/106326121-59
[5.9] [Macros] Code item macros
2 parents dd7552b + 38ff65b commit 9f9e709

24 files changed

+226
-25
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
@@ -7040,6 +7040,11 @@ ERROR(invalid_macro_introduced_name,none,
70407040
ERROR(global_freestanding_macro_script,none,
70417041
"global freestanding macros not yet supported in script mode",
70427042
())
7043+
ERROR(invalid_macro_role_for_macro_syntax,none,
7044+
"invalid macro role for %{a freestanding|an attached}0 macro",
7045+
(unsigned))
7046+
ERROR(macro_cannot_introduce_names,none,
7047+
"'%0' macros are not allowed to introduce names", (StringRef))
70437048

70447049
//------------------------------------------------------------------------------
70457050
// 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
@@ -261,6 +261,7 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
261261
switch (*macroRole) {
262262
case MacroRole::Expression:
263263
case MacroRole::Declaration:
264+
case MacroRole::CodeItem:
264265
case MacroRole::Accessor:
265266
case MacroRole::MemberAttribute:
266267
case MacroRole::Conformance:

lib/AST/ASTWalker.cpp

Lines changed: 11 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,14 @@ 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+
// Visiting the substitute macro expansion decl will visit the same
1351+
// argument list. Skip visiting it again.
1352+
shouldWalkArguments = false;
1353+
}
1354+
13441355
if (shouldWalkArguments && E->getArgs()) {
13451356
ArgumentList *args = doIt(E->getArgs());
13461357
if (!args) return nullptr;

lib/AST/Decl.cpp

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

1007110071
case MacroRole::Conformance:
1007210072
return "conformance";
10073+
10074+
case MacroRole::CodeItem:
10075+
return "codeItem";
1007310076
}
1007410077
}
1007510078

@@ -10111,7 +10114,8 @@ StringRef swift::getMacroIntroducedDeclNameString(
1011110114
static MacroRoles freestandingMacroRoles =
1011210115
(MacroRoles() |
1011310116
MacroRole::Expression |
10114-
MacroRole::Declaration);
10117+
MacroRole::Declaration |
10118+
MacroRole::CodeItem);
1011510119
static MacroRoles attachedMacroRoles = (MacroRoles() |
1011610120
MacroRole::Accessor |
1011710121
MacroRole::MemberAttribute |
@@ -10317,6 +10321,7 @@ void MacroDecl::getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
1031710321
case MacroRole::Declaration:
1031810322
case MacroRole::Member:
1031910323
case MacroRole::Peer:
10324+
case MacroRole::CodeItem:
1032010325
names.push_back(MacroDecl::getUniqueNamePlaceholder(getASTContext()));
1032110326
break;
1032210327

lib/ASTGen/Sources/ASTGen/Macros.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,21 @@ func expandFreestandingMacroInProcess(
606606
evaluatedSyntax = Syntax(CodeBlockItemListSyntax(
607607
decls.map { CodeBlockItemSyntax(item: .decl($0)) }))
608608

609+
case let codeItemMacro as CodeItemMacro.Type:
610+
func expandCodeItemMacro<Node: FreestandingMacroExpansionSyntax>(
611+
_ node: Node
612+
) throws -> [CodeBlockItemSyntax] {
613+
return try codeItemMacro.expansion(
614+
of: sourceManager.detach(
615+
node,
616+
foldingWith: OperatorTable.standardOperators
617+
),
618+
in: context
619+
)
620+
}
621+
let items = try _openExistential(parentExpansion, do: expandCodeItemMacro)
622+
evaluatedSyntax = Syntax(CodeBlockItemListSyntax(items))
623+
609624
default:
610625
print("not an expression macro or a declaration macro")
611626
return nil

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,7 @@ static Optional<MacroRole> getMacroRole(
22042204
auto role = llvm::StringSwitch<Optional<MacroRole>>(roleName->str())
22052205
.Case("declaration", MacroRole::Declaration)
22062206
.Case("expression", MacroRole::Expression)
2207+
.Case("codeItem", MacroRole::CodeItem)
22072208
.Case("accessor", MacroRole::Accessor)
22082209
.Case("memberAttribute", MacroRole::MemberAttribute)
22092210
.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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5394,8 +5394,8 @@ namespace {
53945394
}
53955395
}
53965396
// For a non-expression macro, expand it as a declaration.
5397-
else if (macro->getMacroRoles().contains(MacroRole::Declaration)) {
5398-
auto &ctx = cs.getASTContext();
5397+
else if (macro->getMacroRoles().contains(MacroRole::Declaration) ||
5398+
macro->getMacroRoles().contains(MacroRole::CodeItem)) {
53995399
if (!E->getSubstituteDecl()) {
54005400
auto *med = E->createSubstituteDecl();
54015401
TypeChecker::typeCheckDecl(med);

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
@@ -7007,6 +7008,59 @@ void AttributeChecker::visitRuntimeMetadataAttr(RuntimeMetadataAttr *attr) {
70077008
}
70087009
}
70097010

7011+
void AttributeChecker::visitMacroRoleAttr(MacroRoleAttr *attr) {
7012+
switch (attr->getMacroSyntax()) {
7013+
case MacroSyntax::Freestanding: {
7014+
switch (attr->getMacroRole()) {
7015+
case MacroRole::Expression:
7016+
if (!attr->getNames().empty())
7017+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
7018+
getMacroRoleString(attr->getMacroRole()));
7019+
break;
7020+
case MacroRole::Declaration:
7021+
// TODO: Check names
7022+
break;
7023+
case MacroRole::CodeItem:
7024+
if (!attr->getNames().empty())
7025+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
7026+
getMacroRoleString(attr->getMacroRole()));
7027+
break;
7028+
default:
7029+
diagnoseAndRemoveAttr(attr, diag::invalid_macro_role_for_macro_syntax,
7030+
/*freestanding*/0);
7031+
break;
7032+
}
7033+
break;
7034+
}
7035+
case MacroSyntax::Attached: {
7036+
switch (attr->getMacroRole()) {
7037+
case MacroRole::Accessor:
7038+
// TODO: Check property observer names?
7039+
break;
7040+
case MacroRole::MemberAttribute:
7041+
if (!attr->getNames().empty())
7042+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
7043+
getMacroRoleString(attr->getMacroRole()));
7044+
break;
7045+
case MacroRole::Member:
7046+
break;
7047+
case MacroRole::Peer:
7048+
break;
7049+
case MacroRole::Conformance:
7050+
if (!attr->getNames().empty())
7051+
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
7052+
getMacroRoleString(attr->getMacroRole()));
7053+
break;
7054+
default:
7055+
diagnoseAndRemoveAttr(attr, diag::invalid_macro_role_for_macro_syntax,
7056+
/*attached*/1);
7057+
break;
7058+
}
7059+
break;
7060+
}
7061+
}
7062+
}
7063+
70107064
namespace {
70117065

70127066
class ClosureAttributeChecker

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,7 +2041,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20412041
void visitMacroExpansionDecl(MacroExpansionDecl *MED) {
20422042
// Assign a discriminator.
20432043
(void)MED->getDiscriminator();
2044-
// Expansion already visited as auxiliary decls.
2044+
// Decls in expansion already visited as auxiliary decls.
2045+
MED->forEachExpandedExprOrStmt([&](ASTNode node) {
2046+
TypeChecker::typeCheckASTNode(node, MED->getDeclContext());
2047+
});
20452048
}
20462049

20472050
void visitBoundVariable(VarDecl *VD) {
@@ -3793,12 +3796,10 @@ ExpandMacroExpansionDeclRequest::evaluate(Evaluator &evaluator,
37933796
// If it's not a declaration macro or a code item macro, it must have been
37943797
// parsed as an expression macro, and this decl is just its substitute decl.
37953798
// So there's no thing to be done here.
3796-
if (!roles.contains(MacroRole::Declaration))
3799+
if (!roles.contains(MacroRole::Declaration) &&
3800+
!roles.contains(MacroRole::CodeItem))
37973801
return None;
37983802

3799-
// Otherwise, we treat it as a declaration macro.
3800-
assert(roles.contains(MacroRole::Declaration));
3801-
38023803
// For now, restrict global freestanding macros in script mode.
38033804
if (dc->isModuleScopeContext() &&
38043805
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: 20 additions & 8 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
}
@@ -934,12 +937,20 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) {
934937
return None;
935938
}
936939

937-
// Make sure freestanding macros are enabled before we expand.
938-
if (!ctx.LangOpts.hasFeature(Feature::FreestandingMacros) &&
939-
!macro->getMacroRoles().contains(MacroRole::Expression)) {
940-
med->diagnose(
941-
diag::macro_experimental, "freestanding", "FreestandingMacros");
942-
return None;
940+
// Currently only expression macros are enabled by default. Declaration
941+
// macros need the `FreestandingMacros` feature flag, and code item macros
942+
// need both `FreestandingMacros` and `CodeItemMacros`.
943+
if (!macroRoles.contains(MacroRole::Expression)) {
944+
if (!ctx.LangOpts.hasFeature(Feature::FreestandingMacros)) {
945+
med->diagnose(diag::macro_experimental, "freestanding",
946+
"FreestandingMacros");
947+
return None;
948+
}
949+
if (!macroRoles.contains(MacroRole::Declaration) &&
950+
!ctx.LangOpts.hasFeature(Feature::CodeItemMacros)) {
951+
med->diagnose(diag::macro_experimental, "code item", "CodeItemMacros");
952+
return None;
953+
}
943954
}
944955

945956
#if SWIFT_SWIFT_PARSER
@@ -1284,6 +1295,7 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr,
12841295

12851296
case MacroRole::Expression:
12861297
case MacroRole::Declaration:
1298+
case MacroRole::CodeItem:
12871299
llvm_unreachable("freestanding macro in attached macro evaluation");
12881300
}
12891301

lib/Serialization/Deserialization.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2671,6 +2671,7 @@ getActualMacroRole(uint8_t context) {
26712671
CASE(Member)
26722672
CASE(Peer)
26732673
CASE(Conformance)
2674+
CASE(CodeItem)
26742675
#undef CASE
26752676
}
26762677
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)