Sema: Fix a regression in retroactive conformance diagnostic suppression.

Module qualifying a protocol name in an inheritance clause should suppress
retroactive conformance diagnostics for the protocol and any protocols it
inherits from, just like adding the `@retroactive` attribute. Fixes a
regression in Swift 6.2 introduced by
https://github.com/swiftlang/swift/pull/81576.

Resolves rdar://162295268 and https://github.com/swiftlang/swift/issues/85153.
This commit is contained in:
Allan Shortlidge
2025-10-26 21:22:41 -07:00
parent 8ae717af42
commit 65a3b883f3
2 changed files with 46 additions and 22 deletions

View File

@@ -1720,11 +1720,16 @@ static TypeRepr *unwrapAttributedRepr(TypeRepr *repr) {
return repr;
}
struct ProtocolAndRetroactiveStatus {
ProtocolDecl *proto = nullptr;
bool isMarkedRetroactive = false;
};
static void collectProtocolsFromInheritedEntry(
const InheritedEntry &entry,
Type inheritedTy,
llvm::SmallPtrSetImpl<ProtocolDecl *> &protocolsWithRetroactiveAttr,
SmallVectorImpl<ProtocolDecl *> &protos) {
const InheritedEntry &entry, Type inheritedTy,
SmallVectorImpl<ProtocolAndRetroactiveStatus> &protos) {
bool isMarkedRetroactive = entry.isRetroactive();
if (auto *protoTy = inheritedTy->getAs<ProtocolType>()) {
auto *proto = protoTy->getDecl();
@@ -1732,18 +1737,18 @@ static void collectProtocolsFromInheritedEntry(
// As a fallback, to support previous language versions, also allow
// this through if the protocol has been explicitly module-qualified.
TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr());
if (isModuleQualified(repr, proto->getParentModule())) {
protocolsWithRetroactiveAttr.insert(proto);
}
if (isModuleQualified(repr, proto->getParentModule()))
isMarkedRetroactive = true;
protos.push_back(proto);
protos.push_back({proto, isMarkedRetroactive});
} else if (auto *pct = inheritedTy->getAs<ProtocolCompositionType>()) {
for (auto member : pct->getMembers()) {
collectProtocolsFromInheritedEntry(entry, member,
protocolsWithRetroactiveAttr, protos);
// FIXME: Check for module qualification on each composed protocol.
collectProtocolsFromInheritedEntry(entry, member, protos);
}
} else if (auto *ppt = inheritedTy->getAs<ParameterizedProtocolType>()) {
protos.push_back(ppt->getProtocol());
// FIXME: Check for module qualification.
protos.push_back({ppt->getProtocol(), isMarkedRetroactive});
}
}
@@ -1810,11 +1815,12 @@ static void diagnoseRetroactiveConformances(
continue;
}
SmallVector<ProtocolDecl *, 2> protos;
collectProtocolsFromInheritedEntry(entry, inheritedTy,
protocolsWithRetroactiveAttr, protos);
SmallVector<ProtocolAndRetroactiveStatus, 2> protosAndStatuses;
collectProtocolsFromInheritedEntry(entry, inheritedTy, protosAndStatuses);
for (auto protoAndStatus : protosAndStatuses) {
auto proto = protoAndStatus.proto;
for (auto *proto : protos) {
proto->walkInheritedProtocols([&](ProtocolDecl *decl) {
// If this isn't a retroactive conformance, skip it.
auto found = protocols.find(proto);
@@ -1846,8 +1852,7 @@ static void diagnoseRetroactiveConformances(
return TypeWalker::Action::Continue;
}
// If it's marked @retroactive, no need to warn.
if (entry.isRetroactive()) {
if (protoAndStatus.isMarkedRetroactive) {
// Note that we encountered this protocol through a conformance marked
// @retroactive in case multiple clauses cause the protocol to be
// inherited.

View File

@@ -24,15 +24,20 @@ public struct Sample3 {}
public struct Sample4 {}
public struct Sample5 {}
public struct Sample6 {}
public struct Sample6a {}
public struct Sample6b {}
public struct Sample6c {}
public struct Sample7 {}
public struct Sample8 {}
public struct Sample8a {}
public struct SampleAlreadyConforms: SampleProtocol1 {}
public struct GenericSample1<T> {}
public struct Sample9 {}
public struct Sample10 {}
public struct Sample9a {}
public struct Sample9b {}
#else
@@ -83,9 +88,13 @@ typealias MySample6 = Sample6
extension MySample6: SampleProtocol1 {} // expected-warning {{extension declares a conformance of imported type 'Sample6' to imported protocol 'SampleProtocol1'}}
// expected-note @-1 {{add '@retroactive' to silence this warning}} {{22-37=@retroactive SampleProtocol1}}
// Ensure module-qualifying both types still silences the warning
// Ensure module-qualifying the protocol silences the warning
extension Library.Sample6: Library.SampleProtocol2 {} // ok, module-qualified.
extension Library.Sample6: Library.SampleProtocol2 {} // ok, both types are module-qualified.
extension Sample6a: Library.SampleProtocol2 {} // ok, protocol is module qualified.
extension Library.Sample6b: SampleProtocol2 {} // expected-warning {{extension declares a conformance of imported type 'Sample6b' to imported protocol 'SampleProtocol2'; this will not behave correctly if the owners of 'Library' introduce this conformance in the future}}
// expected-note @-1 {{add '@retroactive' to silence this warning}}
extension Sample6c: Library.SampleProtocol1a {} // ok, protocol is module qualified.
protocol ClientProtocol {}
@@ -125,10 +134,20 @@ extension Sample7: SampleProtocol1 & SampleProtocol2 {}
extension Sample8: @retroactive SampleProtocol1 & SampleProtocol2 {} // ok
// FIXME: Module qualification should suppress this warning
extension Sample8a: Library.SampleProtocol1 & Library.SampleProtocol2 {} // ok
// expected-warning@-1 {{extension declares a conformance of imported type 'Sample8a' to imported protocols 'SampleProtocol1', 'SampleProtocol2'; this will not behave correctly if the owners of 'Library' introduce this conformance in the future}}
// expected-note@-2 {{add '@retroactive' to silence this warning}}
extension Sample9: SampleProtocol3<Int> {}
// expected-warning@-1 {{extension declares a conformance of imported type 'Sample9' to imported protocol 'SampleProtocol3'; this will not behave correctly if the owners of 'Library' introduce this conformance in the future}}
// expected-note@-2 {{add '@retroactive' to silence this warning}}
extension Sample10: @retroactive SampleProtocol3<Int> {}
extension Sample9a: @retroactive SampleProtocol3<Int> {}
#endif
// FIXME: Module qualification should suppress this warning
extension Sample9b: Library.SampleProtocol3<Int> {}
// expected-warning@-1 {{extension declares a conformance of imported type 'Sample9b' to imported protocol 'SampleProtocol3'; this will not behave correctly if the owners of 'Library' introduce this conformance in the future}}
// expected-note@-2 {{add '@retroactive' to silence this warning}}
#endif