Skip to content

Commit 9bff172

Browse files
committed
Sema: Fix diagnoseRetroactiveConformances() to handle protocol compositions
1 parent 758ae18 commit 9bff172

File tree

2 files changed

+79
-71
lines changed

2 files changed

+79
-71
lines changed

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 67 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,7 +1823,6 @@ static void diagnoseRetroactiveConformances(
18231823
return;
18241824
}
18251825

1826-
Type extendedType = ext->getExtendedType();
18271826
NominalTypeDecl *extendedNominalDecl = ext->getExtendedNominal();
18281827
if (!extendedNominalDecl) {
18291828
return;
@@ -1836,88 +1835,88 @@ static void diagnoseRetroactiveConformances(
18361835
return;
18371836
}
18381837

1838+
llvm::SmallSetVector<ProtocolDecl *, 8> externalProtocols;
1839+
for (auto *conformance : ext->getLocalConformances()) {
1840+
if (conformance->isRetroactive()) {
1841+
externalProtocols.insert(conformance->getProtocol());
1842+
}
1843+
}
1844+
18391845
// At this point, we know we're extending a type declared outside this module.
18401846
// We better only be conforming it to protocols declared within this module.
1841-
llvm::SmallSetVector<ProtocolDecl *, 8> externalProtocols;
18421847
llvm::SmallSet<ProtocolDecl *, 8> protocolsWithRetroactiveAttr;
18431848
for (const InheritedEntry &entry : ext->getInherited().getEntries()) {
1844-
if (entry.getType().isNull() || !entry.getTypeRepr()) {
1845-
continue;
1846-
}
1847-
1848-
auto proto =
1849-
dyn_cast_or_null<ProtocolDecl>(entry.getType()->getAnyNominal());
1850-
if (!proto) {
1851-
continue;
1852-
}
1853-
1854-
// As a fallback, to support previous language versions, also allow
1855-
// this through if the protocol has been explicitly module-qualified.
1856-
TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr());
1857-
if (isModuleQualified(repr, proto->getParentModule())) {
1849+
auto inheritedTy = entry.getType();
1850+
if (inheritedTy.isNull() || !entry.getTypeRepr()) {
18581851
continue;
18591852
}
18601853

1861-
proto->walkInheritedProtocols([&](ProtocolDecl *decl) {
1862-
1863-
// Get the original conformance of the extended type to this protocol.
1864-
auto conformanceRef = lookupConformance(extendedType, decl);
1865-
if (!conformanceRef.isConcrete()) {
1866-
return TypeWalker::Action::Continue;
1867-
}
1868-
auto conformance = conformanceRef.getConcrete();
1854+
SmallVector<ProtocolDecl *, 2> protos;
1855+
if (auto *protoTy = inheritedTy->getAs<ProtocolType>()) {
1856+
auto *proto = protoTy->getDecl();
18691857

1870-
// If that conformance came from this extension, then we warn. Otherwise
1871-
// we will have diagnosed it on the extension that actually declares this
1872-
// specific conformance.
1873-
if (conformance->getDeclContext() != ext) {
1874-
return TypeWalker::Action::Continue;
1858+
// As a fallback, to support previous language versions, also allow
1859+
// this through if the protocol has been explicitly module-qualified.
1860+
TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr());
1861+
if (isModuleQualified(repr, proto->getParentModule())) {
1862+
protocolsWithRetroactiveAttr.insert(proto);
1863+
continue;
18751864
}
18761865

1877-
// If this isn't a retroactive conformance, skip it.
1878-
if (!conformance->isRetroactive()) {
1879-
// However, if this is the protocol in the inherited type entry,
1880-
// check to make sure it's not erroneously marked @retroactive when it's
1881-
// not actually retroactive.
1882-
if (decl == proto && entry.isRetroactive()) {
1883-
auto loc =
1884-
entry.getTypeRepr()->findAttrLoc(TypeAttrKind::Retroactive);
1885-
1886-
bool typeInSamePackage = extTypeModule->inSamePackage(module);
1887-
bool typeIsSameModule =
1888-
extTypeModule->isSameModuleLookingThroughOverlays(module);
1889-
1890-
auto declForDiag = (typeIsSameModule || typeInSamePackage)
1891-
? extendedNominalDecl
1892-
: proto;
1893-
bool isSameModule = declForDiag->getParentModule()
1894-
->isSameModuleLookingThroughOverlays(module);
1895-
1896-
diags
1897-
.diagnose(loc, diag::retroactive_attr_does_not_apply, declForDiag,
1898-
isSameModule)
1899-
.warnUntilSwiftVersion(6)
1900-
.fixItRemove(SourceRange(loc, loc.getAdvancedLoc(1)));
1901-
return TypeWalker::Action::Stop;
1866+
protos.push_back(proto);
1867+
} else if (auto *compositionTy = inheritedTy->getAs<ProtocolCompositionType>()) {
1868+
for (auto memberTy : compositionTy->getMembers()) {
1869+
if (auto *protoTy = memberTy->getAs<ProtocolType>()) {
1870+
protos.push_back(protoTy->getDecl());
19021871
}
1903-
return TypeWalker::Action::Continue;
19041872
}
1873+
} else {
1874+
continue;
1875+
}
19051876

1906-
// If it's marked @retroactive, no need to warn.
1907-
if (entry.isRetroactive()) {
1908-
// Note that we encountered this protocol through a conformance marked
1909-
// @retroactive in case multiple clauses cause the protocol to be
1910-
// inherited.
1911-
protocolsWithRetroactiveAttr.insert(decl);
1912-
return TypeWalker::Action::Continue;
1913-
}
1877+
for (auto *proto : protos) {
1878+
proto->walkInheritedProtocols([&](ProtocolDecl *decl) {
1879+
// If this isn't a retroactive conformance, skip it.
1880+
if (!externalProtocols.count(proto)) {
1881+
// However, if this is the protocol in the inherited type entry,
1882+
// check to make sure it's not erroneously marked @retroactive when it's
1883+
// not actually retroactive.
1884+
if (decl == proto && entry.isRetroactive()) {
1885+
auto loc =
1886+
entry.getTypeRepr()->findAttrLoc(TypeAttrKind::Retroactive);
1887+
1888+
bool typeInSamePackage = extTypeModule->inSamePackage(module);
1889+
bool typeIsSameModule =
1890+
extTypeModule->isSameModuleLookingThroughOverlays(module);
1891+
1892+
auto declForDiag = (typeIsSameModule || typeInSamePackage)
1893+
? extendedNominalDecl
1894+
: proto;
1895+
bool isSameModule = declForDiag->getParentModule()
1896+
->isSameModuleLookingThroughOverlays(module);
1897+
1898+
diags
1899+
.diagnose(loc, diag::retroactive_attr_does_not_apply, declForDiag,
1900+
isSameModule)
1901+
.warnUntilSwiftVersion(6)
1902+
.fixItRemove(SourceRange(loc, loc.getAdvancedLoc(1)));
1903+
return TypeWalker::Action::Stop;
1904+
}
1905+
return TypeWalker::Action::Continue;
1906+
}
19141907

1915-
// If we've come this far, we know this extension is the first declaration
1916-
// of the conformance of the extended type to this protocol.
1917-
externalProtocols.insert(decl);
1908+
// If it's marked @retroactive, no need to warn.
1909+
if (entry.isRetroactive()) {
1910+
// Note that we encountered this protocol through a conformance marked
1911+
// @retroactive in case multiple clauses cause the protocol to be
1912+
// inherited.
1913+
protocolsWithRetroactiveAttr.insert(decl);
1914+
return TypeWalker::Action::Continue;
1915+
}
19181916

1919-
return TypeWalker::Action::Continue;
1920-
});
1917+
return TypeWalker::Action::Continue;
1918+
});
1919+
}
19211920
}
19221921

19231922
// Remove protocols that are reachable through a @retroactive conformance.

test/Sema/extension_retroactive_conformances.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public struct Sample3 {}
2020
public struct Sample4 {}
2121
public struct Sample5 {}
2222
public struct Sample6 {}
23+
public struct Sample7 {}
24+
public struct Sample8 {}
2325

2426
public struct SampleAlreadyConforms: SampleProtocol1 {}
2527

@@ -83,9 +85,9 @@ protocol ClientProtocol {}
8385
// ok, conforming a type from another module to a protocol within this module is totally fine
8486
extension Sample1: ClientProtocol {}
8587

86-
struct Sample7: @retroactive SampleProtocol1 {} // expected-error {{'@retroactive' only applies in inheritance clauses in extensions}}{{17-30=}}
88+
struct MySample7: @retroactive SampleProtocol1 {} // expected-error {{'@retroactive' only applies in inheritance clauses in extensions}}{{19-32=}}
8789

88-
extension Sample7: @retroactive ClientProtocol {} // expected-warning {{'retroactive' attribute does not apply; 'Sample7' is declared in this module}}{{20-33=}}
90+
extension MySample7: @retroactive ClientProtocol {} // expected-warning {{'retroactive' attribute does not apply; 'MySample7' is declared in this module}}{{22-35=}}
8991

9092
extension Int: @retroactive ClientProtocol {} // expected-warning {{'retroactive' attribute does not apply; 'ClientProtocol' is declared in this module}}{{16-29=}}
9193

@@ -109,4 +111,11 @@ extension GenericSample1: SampleProtocol1 where T: SampleProtocol1 {}
109111
// expected-warning@-1 {{extension declares a conformance of imported type 'GenericSample1' to imported protocol 'SampleProtocol1'; this will not behave correctly if the owners of 'Library' introduce this conformance in the future}}
110112
// expected-note@-2 {{add '@retroactive' to silence this warning}}
111113

112-
#endif
114+
// Don't forget about protocol compositions
115+
extension Sample7: SampleProtocol1 & SampleProtocol2 {}
116+
// expected-warning@-1 {{extension declares a conformance of imported type 'Sample7' to imported protocols 'SampleProtocol1', 'SampleProtocol2'; this will not behave correctly if the owners of 'Library' introduce this conformance in the future}}
117+
// expected-note@-2 {{add '@retroactive' to silence this warning}}
118+
119+
extension Sample8: @retroactive SampleProtocol1 & SampleProtocol2 {} // ok
120+
121+
#endif

0 commit comments

Comments
 (0)