Skip to content

Commit ef838e4

Browse files
authoredSep 1, 2023
Merge pull request #68175 from DougGregor/extension-conformances-5.9.0
2 parents f3891f2 + aaa442b commit ef838e4

13 files changed

+160
-59
lines changed
 

‎include/swift/AST/DeclContext.h

+4-7
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,15 @@ enum class ConformanceEntryKind : unsigned {
150150
/// Explicitly specified.
151151
Explicit,
152152

153+
/// The conformance is generated by a macro that has not been
154+
/// expanded yet.
155+
PreMacroExpansion,
156+
153157
/// Implicitly synthesized.
154158
Synthesized,
155159

156160
/// Implied by an explicitly-specified conformance.
157161
Implied,
158-
159-
/// The conformance is generated by a macro that has not been
160-
/// expanded yet.
161-
PreMacroExpansion,
162162
};
163163

164164
/// Describes the kind of conformance lookup desired.
@@ -172,9 +172,6 @@ enum class ConformanceLookupKind : unsigned {
172172
/// All conformances except structurally-derived conformances, of which
173173
/// Sendable is the only one.
174174
NonStructural,
175-
/// Exclude conformances added by a macro that has not been expanded
176-
/// yet.
177-
ExcludeUnexpandedMacros,
178175
};
179176

180177
/// Describes a diagnostic for a conflict between two protocol

‎include/swift/AST/ProtocolConformance.h

+2
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,8 @@ class NormalProtocolConformance : public RootProtocolConformance,
552552
assert((sourceKind == ConformanceEntryKind::Implied) ==
553553
(bool)implyingConformance &&
554554
"an implied conformance needs something that implies it");
555+
assert(sourceKind != ConformanceEntryKind::PreMacroExpansion &&
556+
"cannot create conformance pre-macro-expansion");
555557
SourceKindAndImplyingConformance = {implyingConformance, sourceKind};
556558
}
557559

‎lib/AST/ConformanceLookupTable.cpp

+20-6
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,16 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
889889
if (!conformingDC)
890890
return nullptr;
891891

892+
// Never produce a conformance for a pre-macro-expansion conformance. They
893+
// are placeholders that will be superseded.
894+
if (entry->getKind() == ConformanceEntryKind::PreMacroExpansion) {
895+
if (auto supersedingEntry = entry->SupersededBy) {
896+
return getConformance(nominal, supersedingEntry);
897+
}
898+
899+
return nullptr;
900+
}
901+
892902
auto *conformingNominal = conformingDC->getSelfNominalTypeDecl();
893903

894904
// Form the conformance.
@@ -926,6 +936,16 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
926936
? conformingNominal->getLoc()
927937
: cast<ExtensionDecl>(conformingDC)->getLoc();
928938

939+
NormalProtocolConformance *implyingConf = nullptr;
940+
if (entry->Source.getKind() == ConformanceEntryKind::Implied) {
941+
auto implyingEntry = entry->Source.getImpliedSource();
942+
auto origImplyingConf = getConformance(conformingNominal, implyingEntry);
943+
if (!origImplyingConf)
944+
return nullptr;
945+
946+
implyingConf = origImplyingConf->getRootNormalConformance();
947+
}
948+
929949
// Create or find the normal conformance.
930950
auto normalConf =
931951
ctx.getConformance(conformingType, protocol, conformanceLoc,
@@ -936,12 +956,6 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
936956
// early return at the start of this function.
937957
entry->Conformance = normalConf;
938958

939-
NormalProtocolConformance *implyingConf = nullptr;
940-
if (entry->Source.getKind() == ConformanceEntryKind::Implied) {
941-
auto implyingEntry = entry->Source.getImpliedSource();
942-
implyingConf = getConformance(conformingNominal, implyingEntry)
943-
->getRootNormalConformance();
944-
}
945959
normalConf->setSourceKindAndImplyingConformance(entry->Source.getKind(),
946960
implyingConf);
947961

‎lib/AST/ConformanceLookupTable.h

+17-6
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
147147
///
148148
/// The only difference between the ranking kind and the kind is
149149
/// that implied conformances originating from a synthesized
150-
/// conformance are considered to be synthesized (which has a
150+
/// or pre-macro-expansion conformance are considered to be synthesized (which has a
151151
/// lower ranking).
152152
ConformanceEntryKind getRankingKind() const {
153153
switch (auto kind = getKind()) {
@@ -157,11 +157,22 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
157157
case ConformanceEntryKind::PreMacroExpansion:
158158
return kind;
159159

160-
case ConformanceEntryKind::Implied:
161-
return (getImpliedSource()->getDeclaredConformance()->getKind()
162-
== ConformanceEntryKind::Synthesized)
163-
? ConformanceEntryKind::Synthesized
164-
: ConformanceEntryKind::Implied;
160+
case ConformanceEntryKind::Implied: {
161+
auto impliedSourceKind =
162+
getImpliedSource()->getDeclaredConformance()->getKind();
163+
switch (impliedSourceKind) {
164+
case ConformanceEntryKind::Synthesized:
165+
case ConformanceEntryKind::PreMacroExpansion:
166+
return impliedSourceKind;
167+
168+
case ConformanceEntryKind::Explicit:
169+
case ConformanceEntryKind::Inherited:
170+
return ConformanceEntryKind::Implied;
171+
172+
case ConformanceEntryKind::Implied:
173+
return getImpliedSource()->getRankingKind();
174+
}
175+
}
165176
}
166177

167178
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");

‎lib/AST/NameLookup.cpp

+29-7
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,17 @@ namespace {
17571757
};
17581758
}
17591759

1760+
/// Given an extension declaration, return the extended nominal type if the
1761+
/// extension was produced by expanding an extension or conformance macro from
1762+
/// the nominal declaration itself.
1763+
static NominalTypeDecl *nominalForExpandedExtensionDecl(ExtensionDecl *ext) {
1764+
if (!ext->isInMacroExpansionInContext())
1765+
return nullptr;
1766+
1767+
1768+
return ext->getSelfNominalTypeDecl();
1769+
}
1770+
17601771
PotentialMacroExpansions PotentialMacroExpansionsInContextRequest::evaluate(
17611772
Evaluator &evaluator, TypeOrExtensionDecl container) const {
17621773
/// The implementation here needs to be kept in sync with
@@ -1767,6 +1778,15 @@ PotentialMacroExpansions PotentialMacroExpansionsInContextRequest::evaluate(
17671778
auto containerDecl = container.getAsDecl();
17681779
forEachPotentialAttachedMacro(containerDecl, MacroRole::Member, nameTracker);
17691780

1781+
// If the container is an extension that was created from an extension macro,
1782+
// look at the nominal declaration to find any extension macros.
1783+
if (auto ext = dyn_cast<ExtensionDecl>(containerDecl)) {
1784+
if (auto nominal = nominalForExpandedExtensionDecl(ext)) {
1785+
forEachPotentialAttachedMacro(
1786+
nominal, MacroRole::Extension, nameTracker);
1787+
}
1788+
}
1789+
17701790
// Peer and freestanding declaration macros.
17711791
auto dc = container.getAsDeclContext();
17721792
auto idc = container.getAsIterableDeclContext();
@@ -1825,13 +1845,15 @@ populateLookupTableEntryFromMacroExpansions(ASTContext &ctx,
18251845
// names match.
18261846
{
18271847
MacroIntroducedNameTracker nameTracker;
1828-
if (auto nominal = dyn_cast<NominalTypeDecl>(container.getAsDecl())) {
1829-
forEachPotentialAttachedMacro(nominal, MacroRole::Extension, nameTracker);
1830-
if (nameTracker.shouldExpandForName(name)) {
1831-
(void)evaluateOrDefault(
1832-
ctx.evaluator,
1833-
ExpandExtensionMacros{nominal},
1834-
false);
1848+
if (auto ext = dyn_cast<ExtensionDecl>(container.getAsDecl())) {
1849+
if (auto nominal = nominalForExpandedExtensionDecl(ext)) {
1850+
forEachPotentialAttachedMacro(nominal, MacroRole::Extension, nameTracker);
1851+
if (nameTracker.shouldExpandForName(name)) {
1852+
(void)evaluateOrDefault(
1853+
ctx.evaluator,
1854+
ExpandExtensionMacros{nominal},
1855+
false);
1856+
}
18351857
}
18361858
}
18371859
}

‎lib/AST/ProtocolConformance.cpp

+3-15
Original file line numberDiff line numberDiff line change
@@ -1302,8 +1302,8 @@ IterableDeclContext::getLocalConformances(ConformanceLookupKind lookupKind)
13021302
switch (conformance->getSourceKind()) {
13031303
case ConformanceEntryKind::Explicit:
13041304
case ConformanceEntryKind::Synthesized:
1305+
return true;
13051306
case ConformanceEntryKind::PreMacroExpansion:
1306-
return true;
13071307
case ConformanceEntryKind::Implied:
13081308
case ConformanceEntryKind::Inherited:
13091309
return false;
@@ -1314,34 +1314,22 @@ IterableDeclContext::getLocalConformances(ConformanceLookupKind lookupKind)
13141314
case ConformanceEntryKind::Explicit:
13151315
case ConformanceEntryKind::Synthesized:
13161316
case ConformanceEntryKind::Implied:
1317-
case ConformanceEntryKind::PreMacroExpansion:
13181317
return true;
13191318
case ConformanceEntryKind::Inherited:
1319+
case ConformanceEntryKind::PreMacroExpansion:
13201320
return false;
13211321
}
13221322

13231323
case ConformanceLookupKind::All:
13241324
case ConformanceLookupKind::NonStructural:
13251325
return true;
1326-
1327-
case ConformanceLookupKind::ExcludeUnexpandedMacros:
1328-
switch (conformance->getSourceKind()) {
1329-
case ConformanceEntryKind::PreMacroExpansion:
1330-
return false;
1331-
case ConformanceEntryKind::Explicit:
1332-
case ConformanceEntryKind::Synthesized:
1333-
case ConformanceEntryKind::Implied:
1334-
case ConformanceEntryKind::Inherited:
1335-
return true;
1336-
}
13371326
}
13381327
});
13391328

13401329
// If we want to add structural conformances, do so now.
13411330
switch (lookupKind) {
13421331
case ConformanceLookupKind::All:
1343-
case ConformanceLookupKind::NonInherited:
1344-
case ConformanceLookupKind::ExcludeUnexpandedMacros: {
1332+
case ConformanceLookupKind::NonInherited: {
13451333
// Look for a Sendable conformance globally. If it is synthesized
13461334
// and matches this declaration context, use it.
13471335
auto dc = getAsGenericContext();

‎lib/SIL/IR/SILSymbolVisitor.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,6 @@ class SILSymbolVisitorImpl : public ASTVisitor<SILSymbolVisitorImpl> {
238238
void addConformances(const IterableDeclContext *IDC) {
239239
for (auto conformance :
240240
IDC->getLocalConformances(ConformanceLookupKind::NonInherited)) {
241-
if (conformance->getSourceKind() == ConformanceEntryKind::PreMacroExpansion)
242-
continue;
243-
244241
auto protocol = conformance->getProtocol();
245242
if (Ctx.getOpts().PublicSymbolsOnly &&
246243
getDeclLinkage(protocol) != FormalLinkage::PublicUnique)

‎lib/SILGen/SILGenType.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -1132,9 +1132,6 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {
11321132
// are existential and do not have witness tables.
11331133
for (auto *conformance : theType->getLocalConformances(
11341134
ConformanceLookupKind::NonInherited)) {
1135-
if (conformance->getSourceKind() == ConformanceEntryKind::PreMacroExpansion)
1136-
continue;
1137-
11381135
assert(conformance->isComplete());
11391136
if (auto *normal = dyn_cast<NormalProtocolConformance>(conformance))
11401137
SGM.getWitnessTable(normal);

‎lib/Sema/TypeCheckMacros.cpp

+1-9
Original file line numberDiff line numberDiff line change
@@ -1593,15 +1593,7 @@ llvm::Optional<unsigned> swift::expandExtensions(CustomAttr *attr,
15931593
for (auto protocol : potentialConformances) {
15941594
SmallVector<ProtocolConformance *, 2> existingConformances;
15951595
nominal->lookupConformance(protocol, existingConformances);
1596-
1597-
bool hasExistingConformance = llvm::any_of(
1598-
existingConformances,
1599-
[&](ProtocolConformance *conformance) {
1600-
return conformance->getSourceKind() !=
1601-
ConformanceEntryKind::PreMacroExpansion;
1602-
});
1603-
1604-
if (!hasExistingConformance) {
1596+
if (existingConformances.empty()) {
16051597
introducedConformances.push_back(protocol);
16061598
}
16071599
}

‎lib/Sema/TypeCheckProtocol.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -6443,8 +6443,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
64436443
const auto defaultAccess = nominal->getFormalAccess();
64446444

64456445
// Check each of the conformances associated with this context.
6446-
auto conformances = idc->getLocalConformances(
6447-
ConformanceLookupKind::ExcludeUnexpandedMacros);
6446+
auto conformances = idc->getLocalConformances();
64486447

64496448
// The conformance checker bundle that checks all conformances in the context.
64506449
auto &Context = dc->getASTContext();

‎test/IDE/complete_optionset.swift

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public macro OptionSet<RawType>() =
2828
// MEMBER_STATIC: Decl[StaticVar]/CurrNominal: secondDay[#ShippingOptions#]; name=secondDay
2929
// MEMBER_STATIC: Decl[StaticVar]/CurrNominal: priority[#ShippingOptions#]; name=priority
3030
// MEMBER_STATIC: Decl[StaticVar]/CurrNominal: standard[#ShippingOptions#]; name=standard
31-
// MEMBER_STATIC: Decl[TypeAlias]/CurrNominal: ArrayLiteralElement[#ShippingOptions#]; name=ArrayLiteralElement
3231
// MEMBER_STATIC: Decl[TypeAlias]/CurrNominal: Element[#ShippingOptions#]; name=Element
3332
// MEMBER_STATIC: Decl[Constructor]/Super/IsSystem: init()[#ShippingOptions#]; name=init()
3433
// MEMBER_STATIC: Decl[Constructor]/Super/IsSystem: init({#(sequence): Sequence#})[#ShippingOptions#]; name=init(:)

‎test/Macros/Inputs/syntax_macro_definitions.swift

+46
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,39 @@ public struct EquatableMacro: ConformanceMacro {
13351335
}
13361336
}
13371337

1338+
public struct EquatableViaMembersMacro: ExtensionMacro {
1339+
public static func expansion(
1340+
of node: AttributeSyntax,
1341+
attachedTo decl: some DeclGroupSyntax,
1342+
providingExtensionsOf type: some TypeSyntaxProtocol,
1343+
conformingTo protocols: [TypeSyntax],
1344+
in context: some MacroExpansionContext
1345+
) throws -> [ExtensionDeclSyntax] {
1346+
let comparisons: [String] = decl.storedProperties().map { property in
1347+
guard let binding = property.bindings.first,
1348+
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier else {
1349+
return "true"
1350+
}
1351+
1352+
return "lhs.\(identifier) == rhs.\(identifier)"
1353+
}
1354+
1355+
let condition = comparisons.joined(separator: " && ")
1356+
let equalOperator: DeclSyntax = """
1357+
static func ==(lhs: \(type.trimmed), rhs: \(type.trimmed)) -> Bool {
1358+
return \(raw: condition)
1359+
}
1360+
"""
1361+
1362+
let ext: DeclSyntax = """
1363+
extension \(type.trimmed): Equatable {
1364+
\(equalOperator)
1365+
}
1366+
"""
1367+
return [ext.cast(ExtensionDeclSyntax.self)]
1368+
}
1369+
}
1370+
13381371
public struct ConformanceViaExtensionMacro: ExtensionMacro {
13391372
public static func expansion(
13401373
of node: AttributeSyntax,
@@ -1370,6 +1403,19 @@ public struct HashableMacro: ConformanceMacro {
13701403
}
13711404
}
13721405

1406+
public struct ImpliesHashableMacro: ExtensionMacro {
1407+
public static func expansion(
1408+
of node: AttributeSyntax,
1409+
attachedTo: some DeclGroupSyntax,
1410+
providingExtensionsOf type: some TypeSyntaxProtocol,
1411+
conformingTo protocols: [TypeSyntax],
1412+
in context: some MacroExpansionContext
1413+
) throws -> [ExtensionDeclSyntax] {
1414+
let ext: DeclSyntax = "extension \(type.trimmed): ImpliesHashable {}"
1415+
return [ext.cast(ExtensionDeclSyntax.self)]
1416+
}
1417+
}
1418+
13731419
public struct DelegatedConformanceMacro: ConformanceMacro, MemberMacro {
13741420
public static func expansion(
13751421
of node: AttributeSyntax,

‎test/Macros/macro_expand_extensions.swift

+37
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
// RUN: not %target-swift-frontend -enable-experimental-feature ExtensionMacros -swift-version 5 -typecheck -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -serialize-diagnostics-path %t/macro_expand.dia %s -emit-macro-expansion-files no-diagnostics
1111
// RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK-DIAGS %s
1212

13+
// Ensure that we can serialize this file as a module.
14+
// RUN: %target-swift-frontend -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) %s -I %t -disable-availability-checking -emit-module -o %t/MyModule.swiftmodule -enable-testing
15+
1316
// RUN: %target-build-swift -enable-experimental-feature ExtensionMacros -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) %s -o %t/main -module-name MacroUser -swift-version 5 -emit-tbd -emit-tbd-path %t/MacroUser.tbd -I %t
1417
// RUN: %target-codesign %t/main
1518
// RUN: %target-run %t/main | %FileCheck %s
@@ -165,3 +168,37 @@ macro AvailableEquatable() = #externalMacro(module: "MacroDefinition", type: "Co
165168
struct TestAvailability {
166169
static let x : any Equatable.Type = TestAvailability.self
167170
}
171+
172+
@attached(extension, conformances: Equatable, names: named(==))
173+
macro Equatable() = #externalMacro(module: "MacroDefinition", type: "EquatableViaMembersMacro")
174+
175+
@propertyWrapper
176+
struct NotEquatable<T> {
177+
var wrappedValue: T
178+
}
179+
180+
@Equatable
181+
struct HasPropertyWrappers {
182+
@NotEquatable
183+
var value: Int = 0
184+
}
185+
186+
func requiresEquatable<T: Equatable>(_: T) { }
187+
func testHasPropertyWrappers(hpw: HasPropertyWrappers) {
188+
requiresEquatable(hpw)
189+
}
190+
191+
// Check that conformances implied by a macro-defined conformance are serialized
192+
// without issue.
193+
public protocol ImpliesHashable: Hashable { }
194+
195+
@attached(extension, conformances: ImpliesHashable)
196+
macro ImpliesHashable() = #externalMacro(module: "MacroDefinition", type: "ImpliesHashableMacro")
197+
198+
func requiresHashable<T: Hashable>(_: T) { }
199+
func testMakeMeHashable(mmh: MakeMeHashable, dict: [MakeMeHashable: Int]) {
200+
requiresHashable(mmh)
201+
}
202+
203+
@ImpliesHashable
204+
public struct MakeMeHashable { }

0 commit comments

Comments
 (0)