Skip to content

Commit f8f8d24

Browse files
authored
Merge pull request #67758 from DougGregor/member-macro-conformances
2 parents 0e1cdb5 + 596da31 commit f8f8d24

11 files changed

+148
-29
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
> **Note**\
44
> This is in reverse chronological order, so newer entries are added to the top.
55
6+
## Swift 5.9.2
7+
8+
* [SE-0407][]:
9+
10+
Member macros can specify a list of protocols via the `conformances` argument to the macro role. The macro implementation will be provided with those protocols that are listed but have not already been implemented by the type to which the member macro is attached, in the same manner as extension macros.
11+
12+
```swift
13+
@attached(member, conformances: Decodable, Encodable, names: named(init(from:), encode(to:)))
14+
@attached(extension, conformances: Decodable, Encodable, names: named(init(from:), encode(to:)))
15+
macro Codable() = #externalMacro(module: "MyMacros", type: "CodableMacro")
16+
```
17+
618
## Swift 5.9
719

820
* [SE-0382][], [SE-0389][], [SE-0394][], [SE-0397][]:
@@ -9833,6 +9845,7 @@ using the `.dynamicType` member to retrieve the type of an expression should mig
98339845
[SE-0389]: https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md
98349846
[SE-0394]: https://github.com/apple/swift-evolution/blob/main/proposals/0394-swiftpm-expression-macros.md
98359847
[SE-0397]: https://github.com/apple/swift-evolution/blob/main/proposals/0397-freestanding-declaration-macros.md
9848+
[SE-0407]: https://github.com/apple/swift-evolution/blob/main/proposals/0407-member-macro-conformances.md
98369849
[#64927]: <https://github.com/apple/swift/issues/64927>
98379850
[#42697]: <https://github.com/apple/swift/issues/42697>
98389851
[#42728]: <https://github.com/apple/swift/issues/42728>

include/swift/AST/Decl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8702,6 +8702,7 @@ class MacroDecl : public GenericContext, public ValueDecl {
87028702
/// be added if this macro does not contain an extension role.
87038703
void getIntroducedConformances(
87048704
NominalTypeDecl *attachedTo,
8705+
MacroRole role,
87058706
SmallVectorImpl<ProtocolDecl *> &conformances) const;
87068707

87078708
/// Returns a DeclName that represents arbitrary names.

include/swift/AST/TypeCheckRequests.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3338,10 +3338,10 @@ class ResolveMacroRequest
33383338
void noteCycleStep(DiagnosticEngine &diags) const;
33393339
};
33403340

3341-
/// Returns the resolved constraint types that an extension macro
3342-
/// adds conformances to.
3343-
class ResolveExtensionMacroConformances
3344-
: public SimpleRequest<ResolveExtensionMacroConformances,
3341+
/// Returns the resolved constraint types that a macro references conformances
3342+
/// to.
3343+
class ResolveMacroConformances
3344+
: public SimpleRequest<ResolveMacroConformances,
33453345
ArrayRef<Type>(const MacroRoleAttr *, const Decl *),
33463346
RequestFlags::Cached> {
33473347
public:

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest,
363363
SWIFT_REQUEST(TypeChecker, ResolveMacroRequest,
364364
ConcreteDeclRef(UnresolvedMacroReference, const Decl *),
365365
Cached, NoLocationInfo)
366-
SWIFT_REQUEST(TypeChecker, ResolveExtensionMacroConformances,
366+
SWIFT_REQUEST(TypeChecker, ResolveMacroConformances,
367367
ArrayRef<Type>(const MacroRoleAttr *, const Decl *),
368368
Cached, NoLocationInfo)
369369
SWIFT_REQUEST(TypeChecker, ResolveTypeEraserTypeRequest,

lib/AST/Attr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
13931393
// Print conformances, if present.
13941394
auto conformances = evaluateOrDefault(
13951395
D->getASTContext().evaluator,
1396-
ResolveExtensionMacroConformances{Attr, D},
1396+
ResolveMacroConformances{Attr, D},
13971397
{});
13981398
if (!conformances.empty()) {
13991399
Printer << ", conformances: ";

lib/AST/ConformanceLookupTable.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,8 @@ void ConformanceLookupTable::addMacroGeneratedProtocols(
506506
MacroRole::Extension,
507507
[&](CustomAttr *attr, MacroDecl *macro) {
508508
SmallVector<ProtocolDecl *, 2> conformances;
509-
macro->getIntroducedConformances(nominal, conformances);
509+
macro->getIntroducedConformances(
510+
nominal, MacroRole::Extension, conformances);
510511

511512
for (auto *protocol : conformances) {
512513
addProtocol(protocol, attr->getLocation(), source);

lib/AST/Decl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10889,15 +10889,16 @@ void MacroDecl::getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
1088910889

1089010890
void MacroDecl::getIntroducedConformances(
1089110891
NominalTypeDecl *attachedTo,
10892+
MacroRole role,
1089210893
SmallVectorImpl<ProtocolDecl *> &conformances) const {
10893-
auto *attr = getMacroRoleAttr(MacroRole::Extension);
10894+
auto *attr = getMacroRoleAttr(role);
1089410895
if (!attr)
1089510896
return;
1089610897

1089710898
auto &ctx = getASTContext();
1089810899
auto constraintTypes = evaluateOrDefault(
1089910900
ctx.evaluator,
10900-
ResolveExtensionMacroConformances{attr, this},
10901+
ResolveMacroConformances{attr, this},
1090110902
{});
1090210903

1090310904
for (auto constraint : constraintTypes) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6948,7 +6948,7 @@ void AttributeChecker::visitMacroRoleAttr(MacroRoleAttr *attr) {
69486948

69496949
(void)evaluateOrDefault(
69506950
Ctx.evaluator,
6951-
ResolveExtensionMacroConformances{attr, D},
6951+
ResolveMacroConformances{attr, D},
69526952
{});
69536953
}
69546954

lib/Sema/TypeCheckMacros.cpp

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,12 +1548,56 @@ swift::expandAttributes(CustomAttr *attr, MacroDecl *macro, Decl *member) {
15481548
return macroSourceFile->getBufferID();
15491549
}
15501550

1551+
// Collect the protocol conformances that the macro asked about but were
1552+
// not already present on the declaration.
1553+
static TinyPtrVector<ProtocolDecl *> getIntroducedConformances(
1554+
NominalTypeDecl *nominal, MacroRole role, MacroDecl *macro,
1555+
SmallVectorImpl<ProtocolDecl *> *potentialConformances = nullptr) {
1556+
SmallVector<ProtocolDecl *, 2> potentialConformancesBuffer;
1557+
if (!potentialConformances)
1558+
potentialConformances = &potentialConformancesBuffer;
1559+
macro->getIntroducedConformances(nominal, role, *potentialConformances);
1560+
1561+
TinyPtrVector<ProtocolDecl *> introducedConformances;
1562+
for (auto protocol : *potentialConformances) {
1563+
SmallVector<ProtocolConformance *, 2> existingConformances;
1564+
nominal->lookupConformance(protocol, existingConformances);
1565+
1566+
bool hasExistingConformance = llvm::any_of(
1567+
existingConformances,
1568+
[&](ProtocolConformance *conformance) {
1569+
return conformance->getSourceKind() !=
1570+
ConformanceEntryKind::PreMacroExpansion;
1571+
});
1572+
1573+
if (!hasExistingConformance) {
1574+
introducedConformances.push_back(protocol);
1575+
}
1576+
}
1577+
1578+
return introducedConformances;
1579+
}
1580+
15511581
llvm::Optional<unsigned> swift::expandMembers(CustomAttr *attr,
15521582
MacroDecl *macro, Decl *decl) {
1583+
auto nominal = dyn_cast<NominalTypeDecl>(decl);
1584+
if (!nominal) {
1585+
auto ext = dyn_cast<ExtensionDecl>(decl);
1586+
if (!ext)
1587+
return llvm::None;
1588+
1589+
nominal = ext->getExtendedNominal();
1590+
if (!nominal)
1591+
return llvm::None;
1592+
}
1593+
auto introducedConformances = getIntroducedConformances(
1594+
nominal, MacroRole::Member, macro);
1595+
15531596
// Evaluate the macro.
15541597
auto macroSourceFile =
15551598
::evaluateAttachedMacro(macro, decl, attr,
1556-
/*passParentContext=*/false, MacroRole::Member);
1599+
/*passParentContext=*/false, MacroRole::Member,
1600+
introducedConformances);
15571601
if (!macroSourceFile)
15581602
return llvm::None;
15591603

@@ -1625,22 +1669,9 @@ swift::expandExtensions(CustomAttr *attr, MacroDecl *macro,
16251669
return llvm::None;
16261670
}
16271671

1628-
// Collect the protocol conformances that the macro can add. The
1629-
// macro should not add conformances that are already stated in
1630-
// the original source.
1631-
16321672
SmallVector<ProtocolDecl *, 2> potentialConformances;
1633-
macro->getIntroducedConformances(nominal, potentialConformances);
1634-
1635-
SmallVector<ProtocolDecl *, 2> introducedConformances;
1636-
for (auto protocol : potentialConformances) {
1637-
SmallVector<ProtocolConformance *, 2> existingConformances;
1638-
nominal->lookupConformance(protocol, existingConformances);
1639-
if (existingConformances.empty()) {
1640-
introducedConformances.push_back(protocol);
1641-
}
1642-
}
1643-
1673+
auto introducedConformances = getIntroducedConformances(
1674+
nominal, MacroRole::Extension, macro, &potentialConformances);
16441675
auto macroSourceFile = ::evaluateAttachedMacro(macro, nominal, attr,
16451676
/*passParentContext=*/false,
16461677
role, introducedConformances);
@@ -1830,9 +1861,9 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator,
18301861
}
18311862

18321863
ArrayRef<Type>
1833-
ResolveExtensionMacroConformances::evaluate(Evaluator &evaluator,
1834-
const MacroRoleAttr *attr,
1835-
const Decl *decl) const {
1864+
ResolveMacroConformances::evaluate(Evaluator &evaluator,
1865+
const MacroRoleAttr *attr,
1866+
const Decl *decl) const {
18361867
auto *dc = decl->getDeclContext();
18371868
auto &ctx = dc->getASTContext();
18381869

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,3 +2004,53 @@ public struct InitWithProjectedValueWrapperMacro: PeerMacro {
20042004
]
20052005
}
20062006
}
2007+
2008+
public struct RequiredDefaultInitMacro: ExtensionMacro {
2009+
public static func expansion(
2010+
of node: AttributeSyntax,
2011+
attachedTo decl: some DeclGroupSyntax,
2012+
providingExtensionsOf type: some TypeSyntaxProtocol,
2013+
conformingTo protocols: [TypeSyntax],
2014+
in context: some MacroExpansionContext
2015+
) throws -> [ExtensionDeclSyntax] {
2016+
if protocols.isEmpty {
2017+
return []
2018+
}
2019+
2020+
let decl: DeclSyntax =
2021+
"""
2022+
extension \(type.trimmed): DefaultInit {
2023+
}
2024+
2025+
"""
2026+
2027+
return [
2028+
decl.cast(ExtensionDeclSyntax.self)
2029+
]
2030+
}
2031+
}
2032+
2033+
extension RequiredDefaultInitMacro: MemberMacro {
2034+
public static func expansion(
2035+
of node: AttributeSyntax,
2036+
providingMembersOf declaration: some DeclGroupSyntax,
2037+
in context: some MacroExpansionContext
2038+
) throws -> [DeclSyntax] {
2039+
fatalError("old swift-syntax")
2040+
}
2041+
2042+
public static func expansion(
2043+
of node: AttributeSyntax,
2044+
providingMembersOf declaration: some DeclGroupSyntax,
2045+
conformingTo protocols: [TypeSyntax],
2046+
in context: some MacroExpansionContext
2047+
) throws -> [DeclSyntax] {
2048+
let decl: DeclSyntax
2049+
if declaration.is(ClassDeclSyntax.self) && protocols.isEmpty {
2050+
decl = "required init() { }"
2051+
} else {
2052+
decl = "init() { }"
2053+
}
2054+
return [ decl ]
2055+
}
2056+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// REQUIRES: swift_swift_parser
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath
5+
6+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature ExtensionMacros -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -swift-version 5 -I %t
7+
protocol DefaultInit {
8+
init()
9+
}
10+
11+
@attached(extension, conformances: DefaultInit)
12+
@attached(member, conformances: DefaultInit, names: named(init()))
13+
macro DefaultInit() = #externalMacro(module: "MacroDefinition", type: "RequiredDefaultInitMacro")
14+
15+
@DefaultInit
16+
class C { }
17+
18+
@DefaultInit
19+
class D: C { }
20+
21+
@DefaultInit
22+
struct E { }

0 commit comments

Comments
 (0)