Skip to content

Commit 1e35fff

Browse files
committed
Sema: Fix diagnoseRetroactiveConformances() to handle protocol compositions
1 parent 2913255 commit 1e35fff

File tree

2 files changed

+87
-76
lines changed

2 files changed

+87
-76
lines changed

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 76 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,106 +1824,108 @@ static void diagnoseRetroactiveConformances(
18241824
return;
18251825
}
18261826

1827-
Type extendedType = ext->getExtendedType();
18281827
NominalTypeDecl *extendedNominalDecl = ext->getExtendedNominal();
1829-
if (!extendedNominalDecl) {
1828+
if (!extendedNominalDecl || isa<BuiltinTupleDecl>(extendedNominalDecl))
18301829
return;
1831-
}
18321830

18331831
ModuleDecl *extTypeModule = extendedNominalDecl->getParentModule();
18341832

18351833
// If the type comes from the __ObjC clang header module, don't warn.
1836-
if (extTypeModule->getName().is(CLANG_HEADER_MODULE_NAME)) {
1834+
if (extTypeModule->getName().is(CLANG_HEADER_MODULE_NAME))
18371835
return;
1838-
}
18391836

18401837
// At this point, we know we're extending a type declared outside this module.
18411838
// We better only be conforming it to protocols declared within this module.
1842-
llvm::SmallSetVector<ProtocolDecl *, 8> externalProtocols;
1839+
llvm::SmallMapVector<ProtocolDecl *, bool, 8> protocols;
18431840
llvm::SmallSet<ProtocolDecl *, 8> protocolsWithRetroactiveAttr;
1844-
for (const InheritedEntry &entry : ext->getInherited().getEntries()) {
1845-
if (entry.getType().isNull() || !entry.getTypeRepr()) {
1846-
continue;
1847-
}
18481841

1849-
auto proto =
1850-
dyn_cast_or_null<ProtocolDecl>(entry.getType()->getAnyNominal());
1851-
if (!proto) {
1852-
continue;
1853-
}
1854-
1855-
// As a fallback, to support previous language versions, also allow
1856-
// this through if the protocol has been explicitly module-qualified.
1857-
TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr());
1858-
if (isModuleQualified(repr, proto->getParentModule())) {
1842+
for (auto *conformance : ext->getLocalConformances()) {
1843+
auto *proto = conformance->getProtocol();
1844+
bool inserted = protocols.insert(std::make_pair(
1845+
proto, conformance->isRetroactive())).second;
1846+
ASSERT(inserted);
1847+
}
1848+
1849+
for (const InheritedEntry &entry : ext->getInherited().getEntries()) {
1850+
auto inheritedTy = entry.getType();
1851+
if (inheritedTy.isNull() || !entry.getTypeRepr()) {
18591852
continue;
18601853
}
18611854

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

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

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

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

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

1920-
return TypeWalker::Action::Continue;
1921-
});
1919+
return TypeWalker::Action::Continue;
1920+
});
1921+
}
19221922
}
19231923

19241924
// Remove protocols that are reachable through a @retroactive conformance.
1925-
for (auto *proto : protocolsWithRetroactiveAttr) {
1926-
externalProtocols.remove(proto);
1925+
SmallSetVector<ProtocolDecl *, 4> externalProtocols;
1926+
for (auto pair : protocols) {
1927+
if (pair.second && !protocolsWithRetroactiveAttr.count(pair.first))
1928+
externalProtocols.insert(pair.first);
19271929
}
19281930

19291931
// If we didn't find any violations, we're done.

test/Sema/extension_retroactive_conformances.swift

Lines changed: 11 additions & 2 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' attribute only applies in inheritance clauses in extensions}}{{17-30=}}
88+
struct MySample7: @retroactive SampleProtocol1 {} // expected-error {{'retroactive' attribute only applies in inheritance clauses in extensions}}
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

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+
112121
#endif

0 commit comments

Comments
 (0)