Skip to content

Commit c867c7c

Browse files
committed
[Macros] Allow extension macros to specify conformances in the attached
attribute.
1 parent 095542a commit c867c7c

File tree

12 files changed

+132
-17
lines changed

12 files changed

+132
-17
lines changed

include/swift/AST/Attr.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2394,36 +2394,45 @@ class ObjCImplementationAttr final : public DeclAttribute {
23942394
/// which declares one of the roles that a given macro can inhabit.
23952395
class MacroRoleAttr final
23962396
: public DeclAttribute,
2397-
private llvm::TrailingObjects<MacroRoleAttr, MacroIntroducedDeclName> {
2397+
private llvm::TrailingObjects<MacroRoleAttr, MacroIntroducedDeclName,
2398+
TypeExpr *> {
23982399
friend TrailingObjects;
23992400

24002401
MacroSyntax syntax;
24012402
MacroRole role;
24022403
unsigned numNames;
2404+
unsigned numConformances;
24032405
SourceLoc lParenLoc, rParenLoc;
24042406

24052407
MacroRoleAttr(SourceLoc atLoc, SourceRange range, MacroSyntax syntax,
24062408
SourceLoc lParenLoc, MacroRole role,
24072409
ArrayRef<MacroIntroducedDeclName> names,
2410+
ArrayRef<TypeExpr *> conformances,
24082411
SourceLoc rParenLoc, bool implicit);
24092412

24102413
public:
24112414
static MacroRoleAttr *create(ASTContext &ctx, SourceLoc atLoc,
24122415
SourceRange range, MacroSyntax syntax,
24132416
SourceLoc lParenLoc, MacroRole role,
24142417
ArrayRef<MacroIntroducedDeclName> names,
2418+
ArrayRef<TypeExpr *> conformances,
24152419
SourceLoc rParenLoc, bool implicit);
24162420

24172421
size_t numTrailingObjects(OverloadToken<MacroIntroducedDeclName>) const {
24182422
return numNames;
24192423
}
24202424

2425+
size_t numTrailingObjects(OverloadToken<TypeExpr *>) const {
2426+
return numConformances;
2427+
}
2428+
24212429
SourceLoc getLParenLoc() const { return lParenLoc; }
24222430
SourceLoc getRParenLoc() const { return rParenLoc; }
24232431

24242432
MacroSyntax getMacroSyntax() const { return syntax; }
24252433
MacroRole getMacroRole() const { return role; }
24262434
ArrayRef<MacroIntroducedDeclName> getNames() const;
2435+
ArrayRef<TypeExpr *> getConformances() const;
24272436
bool hasNameKind(MacroIntroducedDeclNameKind kind) const;
24282437

24292438
static bool classof(const DeclAttribute *DA) {

lib/AST/Attr.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,23 +2491,32 @@ MacroRoleAttr::MacroRoleAttr(SourceLoc atLoc, SourceRange range,
24912491
MacroSyntax syntax, SourceLoc lParenLoc,
24922492
MacroRole role,
24932493
ArrayRef<MacroIntroducedDeclName> names,
2494+
ArrayRef<TypeExpr *> conformances,
24942495
SourceLoc rParenLoc, bool implicit)
24952496
: DeclAttribute(DAK_MacroRole, atLoc, range, implicit),
2496-
syntax(syntax), role(role), numNames(names.size()), lParenLoc(lParenLoc),
2497+
syntax(syntax), role(role), numNames(names.size()),
2498+
numConformances(conformances.size()), lParenLoc(lParenLoc),
24972499
rParenLoc(rParenLoc) {
24982500
auto *trailingNamesBuffer = getTrailingObjects<MacroIntroducedDeclName>();
24992501
std::uninitialized_copy(names.begin(), names.end(), trailingNamesBuffer);
2502+
2503+
auto *trailingConformancesBuffer = getTrailingObjects<TypeExpr *>();
2504+
std::uninitialized_copy(conformances.begin(), conformances.end(),
2505+
trailingConformancesBuffer);
25002506
}
25012507

25022508
MacroRoleAttr *
25032509
MacroRoleAttr::create(ASTContext &ctx, SourceLoc atLoc, SourceRange range,
25042510
MacroSyntax syntax, SourceLoc lParenLoc, MacroRole role,
25052511
ArrayRef<MacroIntroducedDeclName> names,
2512+
ArrayRef<TypeExpr *> conformances,
25062513
SourceLoc rParenLoc, bool implicit) {
2507-
unsigned size = totalSizeToAlloc<MacroIntroducedDeclName>(names.size());
2514+
unsigned size =
2515+
totalSizeToAlloc<MacroIntroducedDeclName, TypeExpr *>(
2516+
names.size(), conformances.size());
25082517
auto *mem = ctx.Allocate(size, alignof(MacroRoleAttr));
25092518
return new (mem) MacroRoleAttr(atLoc, range, syntax, lParenLoc, role, names,
2510-
rParenLoc, implicit);
2519+
conformances, rParenLoc, implicit);
25112520
}
25122521

25132522
ArrayRef<MacroIntroducedDeclName> MacroRoleAttr::getNames() const {
@@ -2517,6 +2526,13 @@ ArrayRef<MacroIntroducedDeclName> MacroRoleAttr::getNames() const {
25172526
};
25182527
}
25192528

2529+
ArrayRef<TypeExpr *> MacroRoleAttr::getConformances() const {
2530+
return {
2531+
getTrailingObjects<TypeExpr *>(),
2532+
numConformances
2533+
};
2534+
}
2535+
25202536
bool MacroRoleAttr::hasNameKind(MacroIntroducedDeclNameKind kind) const {
25212537
return llvm::find_if(getNames(), [kind](MacroIntroducedDeclName name) {
25222538
return name.getKind() == kind;

lib/IDETool/SyntacticMacroExpansion.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ MacroDecl *SyntacticMacroExpansionInstance::getSynthesizedMacroDecl(
163163

164164
auto *attr = MacroRoleAttr::create(ctx, /*atLoc=*/{}, /*range=*/{}, syntax,
165165
/*lParenLoc=*/{}, role, /*names=*/{},
166+
/*conformances=*/{},
166167
/*rParenLoc=*/{}, /*implicit=*/true);
167168
macro->getAttrs().add(attr);
168169
}

lib/Parse/ParseDecl.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,8 +2265,10 @@ Parser::parseMacroRoleAttribute(
22652265
SourceLoc rParenLoc;
22662266
llvm::Optional<MacroRole> role;
22672267
bool sawRole = false;
2268+
bool sawConformances = false;
22682269
bool sawNames = false;
22692270
SmallVector<MacroIntroducedDeclName, 2> names;
2271+
SmallVector<TypeExpr *, 2> conformances;
22702272
auto argumentsStatus = parseList(tok::r_paren, lParenLoc, rParenLoc,
22712273
/*AllowSepAfterLast=*/false,
22722274
diag::expected_rparen_expr_list,
@@ -2295,7 +2297,8 @@ Parser::parseMacroRoleAttribute(
22952297
parseOptionalArgumentLabel(fieldName, fieldNameLoc);
22962298

22972299
// If there is a field name, it better be 'names'.
2298-
if (!(fieldName.empty() || fieldName.is("names"))) {
2300+
if (!(fieldName.empty() || fieldName.is("names") ||
2301+
fieldName.is("conformances"))) {
22992302
diagnose(
23002303
fieldNameLoc, diag::macro_attribute_unknown_label, isAttached,
23012304
fieldName);
@@ -2305,7 +2308,7 @@ Parser::parseMacroRoleAttribute(
23052308

23062309
// If there is no field name and we haven't seen either names or the role,
23072310
// this is the role.
2308-
if (fieldName.empty() && !sawNames && !sawRole) {
2311+
if (fieldName.empty() && !sawConformances && !sawNames && !sawRole) {
23092312
// Whether we saw anything we tried to treat as a role.
23102313
sawRole = true;
23112314

@@ -2350,6 +2353,25 @@ Parser::parseMacroRoleAttribute(
23502353
return status;
23512354
}
23522355

2356+
if (fieldName.is("conformances") ||
2357+
(fieldName.empty() && sawConformances && !sawNames)) {
2358+
if (fieldName.is("conformances") && sawConformances) {
2359+
diagnose(fieldNameLoc.isValid() ? fieldNameLoc : Tok.getLoc(),
2360+
diag::macro_attribute_duplicate_label,
2361+
isAttached,
2362+
"conformances");
2363+
}
2364+
2365+
sawConformances = true;
2366+
2367+
// Parse the introduced conformances
2368+
auto type = parseType();
2369+
auto *typeExpr = new (Context) TypeExpr(type.get());
2370+
conformances.push_back(typeExpr);
2371+
2372+
return status;
2373+
}
2374+
23532375
// If the field name is empty and we haved seen "names", or the field name
23542376
// is "names" but we've already seen the argument label, complain.
23552377
if (fieldName.empty() != sawNames) {
@@ -2453,7 +2475,7 @@ Parser::parseMacroRoleAttribute(
24532475
SourceRange range(Loc, rParenLoc);
24542476
return makeParserResult(MacroRoleAttr::create(
24552477
Context, AtLoc, range, syntax, lParenLoc, *role, names,
2456-
rParenLoc, /*isImplicit*/ false));
2478+
conformances, rParenLoc, /*isImplicit*/ false));
24572479
}
24582480

24592481
/// Guts of \c parseSingleAttrOption and \c parseSingleAttrOptionIdentifier.
@@ -3867,7 +3889,7 @@ ParserStatus Parser::parseDeclAttribute(
38673889
auto attr = MacroRoleAttr::create(
38683890
Context, AtLoc, SourceRange(AtLoc, attrLoc),
38693891
MacroSyntax::Freestanding, SourceLoc(), MacroRole::Expression, { },
3870-
SourceLoc(), /*isImplicit*/ false);
3892+
/*conformances=*/{}, SourceLoc(), /*isImplicit*/ false);
38713893
Attributes.add(attr);
38723894
return makeParserSuccess();
38733895
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7135,6 +7135,25 @@ void AttributeChecker::visitMacroRoleAttr(MacroRoleAttr *attr) {
71357135
break;
71367136
}
71377137
}
7138+
7139+
for (auto *typeExpr : attr->getConformances()) {
7140+
if (auto *typeRepr = typeExpr->getTypeRepr()) {
7141+
auto *dc = D->getDeclContext();
7142+
auto resolved =
7143+
TypeResolution::forInterface(
7144+
dc, TypeResolverContext::GenericRequirement,
7145+
/*unboundTyOpener*/ nullptr,
7146+
/*placeholderHandler*/ nullptr,
7147+
/*packElementOpener*/ nullptr)
7148+
.resolveType(typeRepr);
7149+
7150+
if (resolved->is<ErrorType>()) {
7151+
attr->setInvalid();
7152+
} else {
7153+
typeExpr->setType(MetatypeType::get(resolved));
7154+
}
7155+
}
7156+
}
71387157
}
71397158

71407159
namespace {

lib/Serialization/Deserialization.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2986,6 +2986,7 @@ static llvm::Optional<swift::MacroRole> getActualMacroRole(uint8_t context) {
29862986
CASE(Peer)
29872987
CASE(Conformance)
29882988
CASE(CodeItem)
2989+
CASE(Extension)
29892990
#undef CASE
29902991
}
29912992
return llvm::None;
@@ -5857,14 +5858,15 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
58575858
uint8_t rawMacroSyntax;
58585859
uint8_t rawMacroRole;
58595860
uint64_t numNames;
5861+
uint64_t numConformances;
58605862
ArrayRef<uint64_t> introducedDeclNames;
58615863
serialization::decls_block::MacroRoleDeclAttrLayout::
58625864
readRecord(scratch, isImplicit, rawMacroSyntax, rawMacroRole,
5863-
numNames, introducedDeclNames);
5865+
numNames, numConformances, introducedDeclNames);
58645866
auto role = *getActualMacroRole(rawMacroRole);
58655867
SmallVector<MacroIntroducedDeclName, 1> names;
58665868
unsigned nameIdx = 0;
5867-
while (nameIdx < introducedDeclNames.size()) {
5869+
while (nameIdx < numNames) {
58685870
auto kind = getActualMacroIntroducedDeclNameKind(
58695871
(uint8_t)introducedDeclNames[nameIdx++]);
58705872
auto baseName =
@@ -5889,10 +5891,22 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
58895891
names.push_back(MacroIntroducedDeclName(*kind, name));
58905892
}
58915893

5894+
introducedDeclNames = introducedDeclNames.slice(numNames);
5895+
SmallVector<TypeExpr *, 1> conformances;
5896+
for (TypeID conformanceID : introducedDeclNames) {
5897+
auto conformance = MF.getTypeChecked(conformanceID);
5898+
if (!conformance) {
5899+
return conformance.takeError();
5900+
}
5901+
5902+
conformances.push_back(
5903+
TypeExpr::createImplicit(conformance.get(), ctx));
5904+
}
5905+
58925906
Attr = MacroRoleAttr::create(
58935907
ctx, SourceLoc(), SourceRange(),
58945908
static_cast<MacroSyntax>(rawMacroSyntax), SourceLoc(), role, names,
5895-
SourceLoc(), isImplicit);
5909+
conformances, SourceLoc(), isImplicit);
58965910
break;
58975911
}
58985912

lib/Serialization/ModuleFormat.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 793; // PluginSearchOption
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 794; // extension macros
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -638,7 +638,7 @@ enum class MacroRole : uint8_t {
638638
CodeItem,
639639
Extension,
640640
};
641-
using MacroRoleField = BCFixed<3>;
641+
using MacroRoleField = BCFixed<4>;
642642

643643
// These IDs must \em not be renumbered or reordered without incrementing
644644
// the module version.
@@ -2323,11 +2323,13 @@ namespace decls_block {
23232323
BCFixed<1>, // macro syntax
23242324
MacroRoleField, // macro role
23252325
BCVBR<5>, // number of names
2326+
BCVBR<5>, // number of conformances
23262327
BCArray<IdentifierIDField> // introduced names, where each is encoded as
23272328
// - introduced kind
23282329
// - base name
23292330
// - # of argument labels + 1 (or 0 if none)
23302331
// - argument labels
2332+
// trialed by introduced conformances
23312333
>;
23322334

23332335
#undef SYNTAX_SUGAR_TYPE_LAYOUT

lib/Serialization/Serialization.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3149,10 +3149,19 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
31493149
introducedDeclNames.push_back(S.addDeclBaseNameRef(label));
31503150
}
31513151

3152+
unsigned numNames = introducedDeclNames.size();
3153+
3154+
unsigned numConformances = 0;
3155+
for (auto conformance : theAttr->getConformances()) {
3156+
introducedDeclNames.push_back(
3157+
S.addTypeRef(conformance->getInstanceType()));
3158+
++numConformances;
3159+
}
3160+
31523161
MacroRoleDeclAttrLayout::emitRecord(
31533162
S.Out, S.ScratchRecord, abbrCode, theAttr->isImplicit(),
31543163
static_cast<uint8_t>(theAttr->getMacroSyntax()),
3155-
rawMacroRole, introducedDeclNames.size(),
3164+
rawMacroRole, numNames, numConformances,
31563165
introducedDeclNames);
31573166
return;
31583167
}

test/Macros/macro_expand_extensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// RUN: %target-codesign %t/main
1111
// RUN: %target-run %t/main | %FileCheck %s
1212

13-
@attached(extension, names: named(requirement))
13+
@attached(extension, conformances: P, names: named(requirement))
1414
macro DelegatedConformance() = #externalMacro(module: "MacroDefinition", type: "DelegatedConformanceMacro")
1515

1616
protocol P {

test/Serialization/Inputs/def_macro_plugin.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,23 @@ public struct ArbitraryMembersMacro: MemberMacro {
5151
]
5252
}
5353
}
54+
55+
public struct SendableMacro: ExtensionMacro {
56+
public static func expansion(
57+
of node: AttributeSyntax,
58+
attachedTo: some DeclGroupSyntax,
59+
providingExtensionsOf type: some TypeSyntaxProtocol,
60+
in context: some MacroExpansionContext
61+
) throws -> [ExtensionDeclSyntax] {
62+
let sendableExtension: DeclSyntax =
63+
"""
64+
extension \(type.trimmed): Sendable {}
65+
"""
66+
67+
guard let extensionDecl = sendableExtension.as(ExtensionDeclSyntax.self) else {
68+
return []
69+
}
70+
71+
return [extensionDecl]
72+
}
73+
}

0 commit comments

Comments
 (0)