mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user