Skip to content

Commit b624759

Browse files
authored
Merge pull request #67083 from hborla/5.9-refine-extension-macros
[5.9][Macros] Allow `extension` macros to suppress conformances that are stated in the original source.
2 parents 201c542 + f15948f commit b624759

33 files changed

+457
-213
lines changed

include/swift/AST/Decl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8595,6 +8595,15 @@ class MacroDecl : public GenericContext, public ValueDecl {
85958595
void getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
85968596
SmallVectorImpl<DeclName> &names) const;
85978597

8598+
/// Populate the \c conformances vector with the protocols that
8599+
/// this macro generates conformances to.
8600+
///
8601+
/// Only extension macros can add conformances; no results will
8602+
/// be added if this macro does not contain an extension role.
8603+
void getIntroducedConformances(
8604+
NominalTypeDecl *attachedTo,
8605+
SmallVectorImpl<ProtocolDecl *> &conformances) const;
8606+
85988607
/// Returns a DeclName that represents arbitrary names.
85998608
static DeclName getArbitraryName() {
86008609
return DeclName();

include/swift/AST/DeclContext.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ enum class ConformanceEntryKind : unsigned {
154154

155155
/// Implied by an explicitly-specified conformance.
156156
Implied,
157+
158+
/// The conformance is generated by a macro that has not been
159+
/// expanded yet.
160+
PreMacroExpansion,
157161
};
158162

159163
/// Describes the kind of conformance lookup desired.
@@ -167,6 +171,9 @@ enum class ConformanceLookupKind : unsigned {
167171
/// All conformances except structurally-derived conformances, of which
168172
/// Sendable is the only one.
169173
NonStructural,
174+
/// Exclude conformances added by a macro that has not been expanded
175+
/// yet.
176+
ExcludeUnexpandedMacros,
170177
};
171178

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

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2769,7 +2769,8 @@ NOTE(declared_protocol_conformance_here,none,
27692769
"%select{%0 inherits conformance to protocol %2 from superclass|"
27702770
"%0 declares conformance to protocol %2|"
27712771
"%0 implicitly conforms to protocol %2 (via conformance to %3)|"
2772-
"%0 implicitly conforms to protocol %2}1 here",
2772+
"%0 implicitly conforms to protocol %2 |"
2773+
"conformance to %2 generated by macro }1 here",
27732774
(Type, unsigned, Identifier, Identifier))
27742775

27752776
ERROR(witness_unavailable,none,
@@ -7194,6 +7195,9 @@ ERROR(global_arbitrary_name,none,
71947195
ERROR(local_extension_macro,none,
71957196
"local type cannot have attached extension macro",
71967197
())
7198+
ERROR(extension_macro_invalid_conformance,none,
7199+
"invalid protocol conformance %0 in extension macro",
7200+
(Type))
71977201

71987202
ERROR(macro_resolve_circular_reference, none,
71997203
"circular reference resolving %select{freestanding|attached}0 macro %1",

include/swift/AST/ProtocolConformance.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
440440
///
441441
/// This should never be Inherited: that is handled by
442442
/// InheritedProtocolConformance.
443-
llvm::PointerIntPair<NormalProtocolConformance *, 2, ConformanceEntryKind>
443+
llvm::PointerIntPair<NormalProtocolConformance *, 3, ConformanceEntryKind>
444444
SourceKindAndImplyingConformance = {nullptr,
445445
ConformanceEntryKind::Explicit};
446446

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3337,6 +3337,25 @@ class ResolveMacroRequest
33373337
void noteCycleStep(DiagnosticEngine &diags) const;
33383338
};
33393339

3340+
/// Returns the resolved constraint types that an extension macro
3341+
/// adds conformances to.
3342+
class ResolveExtensionMacroConformances
3343+
: public SimpleRequest<ResolveExtensionMacroConformances,
3344+
ArrayRef<Type>(const MacroRoleAttr *, const Decl *),
3345+
RequestFlags::Cached> {
3346+
public:
3347+
using SimpleRequest::SimpleRequest;
3348+
3349+
private:
3350+
friend SimpleRequest;
3351+
3352+
ArrayRef<Type> evaluate(Evaluator &evaluator,
3353+
const MacroRoleAttr *, const Decl *) const;
3354+
3355+
public:
3356+
bool isCached() const { return true; }
3357+
};
3358+
33403359
class ResolveTypeEraserTypeRequest
33413360
: public SimpleRequest<ResolveTypeEraserTypeRequest,
33423361
Type (ProtocolDecl *, TypeEraserAttr *),
@@ -4032,28 +4051,6 @@ class ExpandAccessorMacros
40324051
void noteCycleStep(DiagnosticEngine &diags) const;
40334052
};
40344053

4035-
/// Expand all conformance macros attached to the given declaration.
4036-
///
4037-
/// Produces the set of macro expansion buffer IDs.
4038-
class ExpandConformanceMacros
4039-
: public SimpleRequest<ExpandConformanceMacros,
4040-
ArrayRef<unsigned>(NominalTypeDecl *),
4041-
RequestFlags::Cached> {
4042-
public:
4043-
using SimpleRequest::SimpleRequest;
4044-
4045-
private:
4046-
friend SimpleRequest;
4047-
4048-
ArrayRef<unsigned> evaluate(Evaluator &evaluator,
4049-
NominalTypeDecl *nominal) const;
4050-
4051-
public:
4052-
bool isCached() const { return true; }
4053-
void diagnoseCycle(DiagnosticEngine &diags) const;
4054-
void noteCycleStep(DiagnosticEngine &diags) const;
4055-
};
4056-
40574054
/// Expand all extension macros attached to the given declaration.
40584055
///
40594056
/// Produces the set of macro expansion buffer IDs.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest,
360360
SWIFT_REQUEST(TypeChecker, ResolveMacroRequest,
361361
ConcreteDeclRef(UnresolvedMacroReference, const Decl *),
362362
Cached, NoLocationInfo)
363+
SWIFT_REQUEST(TypeChecker, ResolveExtensionMacroConformances,
364+
ArrayRef<Type>(const MacroRoleAttr *, const Decl *),
365+
Cached, NoLocationInfo)
363366
SWIFT_REQUEST(TypeChecker, ResolveTypeEraserTypeRequest,
364367
Type(ProtocolDecl *, TypeEraserAttr *),
365368
SeparatelyCached, NoLocationInfo)
@@ -457,9 +460,6 @@ SWIFT_REQUEST(TypeChecker, ExpandMemberAttributeMacros,
457460
SWIFT_REQUEST(TypeChecker, ExpandAccessorMacros,
458461
ArrayRef<unsigned>(AbstractStorageDecl *),
459462
Cached, NoLocationInfo)
460-
SWIFT_REQUEST(TypeChecker, ExpandConformanceMacros,
461-
ArrayRef<unsigned>(NominalTypeDecl *),
462-
Cached, NoLocationInfo)
463463
SWIFT_REQUEST(TypeChecker, ExpandExtensionMacros,
464464
ArrayRef<unsigned>(NominalTypeDecl *),
465465
Cached, NoLocationInfo)

lib/AST/ConformanceLookupTable.cpp

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ DeclContext *ConformanceLookupTable::ConformanceSource::getDeclContext() const {
4343

4444
case ConformanceEntryKind::Synthesized:
4545
return getSynthesizedDeclContext();
46+
47+
case ConformanceEntryKind::PreMacroExpansion:
48+
return getMacroGeneratedDeclContext();
4649
}
4750

4851
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");
@@ -104,6 +107,10 @@ void ConformanceLookupTable::ConformanceEntry::dump(raw_ostream &os,
104107
case ConformanceEntryKind::Synthesized:
105108
os << " synthesized";
106109
break;
110+
111+
case ConformanceEntryKind::PreMacroExpansion:
112+
os << " unexpanded macro";
113+
break;
107114
}
108115

109116
if (auto conf = getConformance()) {
@@ -282,14 +289,8 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
282289
addInheritedProtocols(nominal,
283290
ConformanceSource::forExplicit(nominal));
284291

285-
// Expand conformance macros.
286-
ASTContext &ctx = nominal->getASTContext();
287-
(void)evaluateOrDefault(
288-
ctx.evaluator, ExpandConformanceMacros{nominal}, { });
289-
290-
// Expand extension macros.
291-
(void)evaluateOrDefault(
292-
ctx.evaluator, ExpandExtensionMacros{nominal}, { });
292+
addMacroGeneratedProtocols(
293+
nominal, ConformanceSource::forUnexpandedMacro(nominal));
293294
},
294295
[&](ExtensionDecl *ext,
295296
ArrayRef<ConformanceConstructionInfo> protos) {
@@ -445,6 +446,7 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc,
445446
switch (existingEntry->getKind()) {
446447
case ConformanceEntryKind::Explicit:
447448
case ConformanceEntryKind::Inherited:
449+
case ConformanceEntryKind::PreMacroExpansion:
448450
return false;
449451

450452
case ConformanceEntryKind::Implied:
@@ -495,6 +497,20 @@ void ConformanceLookupTable::addInheritedProtocols(
495497
}
496498
}
497499

500+
void ConformanceLookupTable::addMacroGeneratedProtocols(
501+
NominalTypeDecl *nominal, ConformanceSource source) {
502+
nominal->forEachAttachedMacro(
503+
MacroRole::Extension,
504+
[&](CustomAttr *attr, MacroDecl *macro) {
505+
SmallVector<ProtocolDecl *, 2> conformances;
506+
macro->getIntroducedConformances(nominal, conformances);
507+
508+
for (auto *protocol : conformances) {
509+
addProtocol(protocol, attr->getLocation(), source);
510+
}
511+
});
512+
}
513+
498514
void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal,
499515
DeclContext *dc) {
500516
// Note: recursive type-checking implies that AllConformances
@@ -534,6 +550,7 @@ static bool isReplaceable(ConformanceEntryKind kind) {
534550
switch (kind) {
535551
case ConformanceEntryKind::Implied:
536552
case ConformanceEntryKind::Synthesized:
553+
case ConformanceEntryKind::PreMacroExpansion:
537554
return true;
538555

539556
case ConformanceEntryKind::Explicit:
@@ -562,6 +579,23 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
562579
: Ordering::After);
563580
}
564581

582+
ConformanceEntryKind lhsKind = lhs->getRankingKind();
583+
ConformanceEntryKind rhsKind = rhs->getRankingKind();
584+
585+
// Pre-expanded macro conformances are always superseded by
586+
// conformances written in source. If the conformance is not
587+
// written in the original source, the pre-expanded conformance
588+
// will be superseded by the conformance in the macro expansion
589+
// buffer.
590+
if (lhsKind == ConformanceEntryKind::PreMacroExpansion ||
591+
rhsKind == ConformanceEntryKind::PreMacroExpansion) {
592+
if (lhsKind != rhsKind) {
593+
return (lhs->getKind() < rhs->getKind()
594+
? Ordering::Before
595+
: Ordering::After);
596+
}
597+
}
598+
565599
// If one entry is fixed and the other is not, we have our answer.
566600
if (lhs->isFixed() != rhs->isFixed()) {
567601
auto isReplaceableOrMarker = [](ConformanceEntry *entry) -> bool {
@@ -584,9 +618,6 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
584618
return lhs->isFixed() ? Ordering::Before : Ordering::After;
585619
}
586620

587-
ConformanceEntryKind lhsKind = lhs->getRankingKind();
588-
ConformanceEntryKind rhsKind = rhs->getRankingKind();
589-
590621
if (lhsKind != ConformanceEntryKind::Implied ||
591622
rhsKind != ConformanceEntryKind::Implied) {
592623
// If both conformances are non-replaceable, diagnose the

lib/AST/ConformanceLookupTable.h

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,15 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
8383
/// Describes the "source" of a conformance, indicating where the
8484
/// conformance came from.
8585
class ConformanceSource {
86-
llvm::PointerIntPair<void *, 2, ConformanceEntryKind> Storage;
86+
void *Storage;
87+
88+
ConformanceEntryKind Kind;
8789

8890
/// The location of the "unchecked" attribute, if there is one.
8991
SourceLoc uncheckedLoc;
9092

9193
ConformanceSource(void *ptr, ConformanceEntryKind kind)
92-
: Storage(ptr, kind) { }
94+
: Storage(ptr), Kind(kind) { }
9395

9496
public:
9597
/// Create an inherited conformance.
@@ -126,6 +128,10 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
126128
return ConformanceSource(dc, ConformanceEntryKind::Synthesized);
127129
}
128130

131+
static ConformanceSource forUnexpandedMacro(DeclContext *dc) {
132+
return ConformanceSource(dc, ConformanceEntryKind::PreMacroExpansion);
133+
}
134+
129135
/// Return a new conformance source with the given location of "@unchecked".
130136
ConformanceSource withUncheckedLoc(SourceLoc uncheckedLoc) {
131137
ConformanceSource result(*this);
@@ -135,7 +141,7 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
135141
}
136142

137143
/// Retrieve the kind of conformance formed from this source.
138-
ConformanceEntryKind getKind() const { return Storage.getInt(); }
144+
ConformanceEntryKind getKind() const { return Kind; }
139145

140146
/// Retrieve kind of the conformance for ranking purposes.
141147
///
@@ -148,6 +154,7 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
148154
case ConformanceEntryKind::Explicit:
149155
case ConformanceEntryKind::Inherited:
150156
case ConformanceEntryKind::Synthesized:
157+
case ConformanceEntryKind::PreMacroExpansion:
151158
return kind;
152159

153160
case ConformanceEntryKind::Implied:
@@ -169,28 +176,33 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
169176
/// for the inheriting class.
170177
ClassDecl *getInheritingClass() const {
171178
assert(getKind() == ConformanceEntryKind::Inherited);
172-
return static_cast<ClassDecl *>(Storage.getPointer());
179+
return static_cast<ClassDecl *>(Storage);
173180
}
174181

175182
/// For an explicit conformance, retrieve the declaration context
176183
/// that specifies the conformance.
177184
DeclContext *getExplicitDeclContext() const {
178185
assert(getKind() == ConformanceEntryKind::Explicit);
179-
return static_cast<DeclContext *>(Storage.getPointer());
186+
return static_cast<DeclContext *>(Storage);
187+
}
188+
189+
DeclContext *getMacroGeneratedDeclContext() const {
190+
assert(getKind() == ConformanceEntryKind::PreMacroExpansion);
191+
return static_cast<DeclContext *>(Storage);
180192
}
181193

182194
/// For a synthesized conformance, retrieve the nominal type decl
183195
/// that will receive the conformance.
184196
ConformanceEntry *getImpliedSource() const {
185197
assert(getKind() == ConformanceEntryKind::Implied);
186-
return static_cast<ConformanceEntry *>(Storage.getPointer());
198+
return static_cast<ConformanceEntry *>(Storage);
187199
}
188200

189201
/// For a synthesized conformance, retrieve the nominal type decl
190202
/// that will receive the conformance.
191203
DeclContext *getSynthesizedDeclContext() const {
192204
assert(getKind() == ConformanceEntryKind::Synthesized);
193-
return static_cast<DeclContext *>(Storage.getPointer());
205+
return static_cast<DeclContext *>(Storage);
194206
}
195207

196208
/// Get the declaration context that this conformance will be
@@ -226,6 +238,10 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
226238

227239
/// Whether this conformance is already "fixed" and cannot be superseded.
228240
bool isFixed() const {
241+
// A conformance from an unexpanded macro can always be superseded.
242+
if (getKind() == ConformanceEntryKind::PreMacroExpansion)
243+
return true;
244+
229245
// If a conformance has been assigned, it cannot be superseded.
230246
if (getConformance())
231247
return true;
@@ -235,6 +251,7 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
235251
case ConformanceEntryKind::Explicit:
236252
case ConformanceEntryKind::Implied:
237253
case ConformanceEntryKind::Synthesized:
254+
case ConformanceEntryKind::PreMacroExpansion:
238255
return false;
239256

240257
case ConformanceEntryKind::Inherited:
@@ -341,6 +358,11 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
341358
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
342359
ConformanceSource source);
343360

361+
/// Add the protocols added by attached extension macros that are not
362+
/// yet expanded.
363+
void addMacroGeneratedProtocols(
364+
NominalTypeDecl *nominal, ConformanceSource source);
365+
344366
/// Expand the implied conformances for the given DeclContext.
345367
void expandImpliedConformances(NominalTypeDecl *nominal, DeclContext *dc);
346368

0 commit comments

Comments
 (0)