mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Macros] Allow extension macros to suppress conformances that are already
stated in the original source. If an extension macro can introduce protocol conformances, macro expansion will check which of those protocols already have a stated conformance in the original source. The protocols that don't will be passed as arguments to extension macro expansion, indicating to the macro that it should only add conformances to those protocols.
This commit is contained in:
@@ -43,6 +43,9 @@ DeclContext *ConformanceLookupTable::ConformanceSource::getDeclContext() const {
|
||||
|
||||
case ConformanceEntryKind::Synthesized:
|
||||
return getSynthesizedDeclContext();
|
||||
|
||||
case ConformanceEntryKind::PreMacroExpansion:
|
||||
return getMacroGeneratedDeclContext();
|
||||
}
|
||||
|
||||
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");
|
||||
@@ -104,6 +107,10 @@ void ConformanceLookupTable::ConformanceEntry::dump(raw_ostream &os,
|
||||
case ConformanceEntryKind::Synthesized:
|
||||
os << " synthesized";
|
||||
break;
|
||||
|
||||
case ConformanceEntryKind::PreMacroExpansion:
|
||||
os << " unexpanded macro";
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto conf = getConformance()) {
|
||||
@@ -282,10 +289,8 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
|
||||
addInheritedProtocols(nominal,
|
||||
ConformanceSource::forExplicit(nominal));
|
||||
|
||||
// Expand extension macros.
|
||||
ASTContext &ctx = nominal->getASTContext();
|
||||
(void)evaluateOrDefault(
|
||||
ctx.evaluator, ExpandExtensionMacros{nominal}, { });
|
||||
addMacroGeneratedProtocols(
|
||||
nominal, ConformanceSource::forUnexpandedMacro(nominal));
|
||||
},
|
||||
[&](ExtensionDecl *ext,
|
||||
ArrayRef<ConformanceConstructionInfo> protos) {
|
||||
@@ -441,6 +446,7 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc,
|
||||
switch (existingEntry->getKind()) {
|
||||
case ConformanceEntryKind::Explicit:
|
||||
case ConformanceEntryKind::Inherited:
|
||||
case ConformanceEntryKind::PreMacroExpansion:
|
||||
return false;
|
||||
|
||||
case ConformanceEntryKind::Implied:
|
||||
@@ -491,6 +497,20 @@ void ConformanceLookupTable::addInheritedProtocols(
|
||||
}
|
||||
}
|
||||
|
||||
void ConformanceLookupTable::addMacroGeneratedProtocols(
|
||||
NominalTypeDecl *nominal, ConformanceSource source) {
|
||||
nominal->forEachAttachedMacro(
|
||||
MacroRole::Extension,
|
||||
[&](CustomAttr *attr, MacroDecl *macro) {
|
||||
SmallVector<ProtocolDecl *, 2> conformances;
|
||||
macro->getIntroducedConformances(nominal, conformances);
|
||||
|
||||
for (auto *protocol : conformances) {
|
||||
addProtocol(protocol, attr->getLocation(), source);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal,
|
||||
DeclContext *dc) {
|
||||
// Note: recursive type-checking implies that AllConformances
|
||||
@@ -530,6 +550,7 @@ static bool isReplaceable(ConformanceEntryKind kind) {
|
||||
switch (kind) {
|
||||
case ConformanceEntryKind::Implied:
|
||||
case ConformanceEntryKind::Synthesized:
|
||||
case ConformanceEntryKind::PreMacroExpansion:
|
||||
return true;
|
||||
|
||||
case ConformanceEntryKind::Explicit:
|
||||
@@ -558,6 +579,23 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
|
||||
: Ordering::After);
|
||||
}
|
||||
|
||||
ConformanceEntryKind lhsKind = lhs->getRankingKind();
|
||||
ConformanceEntryKind rhsKind = rhs->getRankingKind();
|
||||
|
||||
// Pre-expanded macro conformances are always superseded by
|
||||
// conformances written in source. If the conformance is not
|
||||
// written in the original source, the pre-expanded conformance
|
||||
// will be superseded by the conformance in the macro expansion
|
||||
// buffer.
|
||||
if (lhsKind == ConformanceEntryKind::PreMacroExpansion ||
|
||||
rhsKind == ConformanceEntryKind::PreMacroExpansion) {
|
||||
if (lhsKind != rhsKind) {
|
||||
return (lhs->getKind() < rhs->getKind()
|
||||
? Ordering::Before
|
||||
: Ordering::After);
|
||||
}
|
||||
}
|
||||
|
||||
// If one entry is fixed and the other is not, we have our answer.
|
||||
if (lhs->isFixed() != rhs->isFixed()) {
|
||||
auto isReplaceableOrMarker = [](ConformanceEntry *entry) -> bool {
|
||||
@@ -580,9 +618,6 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
|
||||
return lhs->isFixed() ? Ordering::Before : Ordering::After;
|
||||
}
|
||||
|
||||
ConformanceEntryKind lhsKind = lhs->getRankingKind();
|
||||
ConformanceEntryKind rhsKind = rhs->getRankingKind();
|
||||
|
||||
if (lhsKind != ConformanceEntryKind::Implied ||
|
||||
rhsKind != ConformanceEntryKind::Implied) {
|
||||
// If both conformances are non-replaceable, diagnose the
|
||||
|
||||
Reference in New Issue
Block a user