Skip to content

Commit a723538

Browse files
committed
Sema: Only diagnose retroactive conformances once.
A type may conform to a single protocol via multiple inheritance clauses on the same extension. When this occurs, we must be careful to only diagnose that conformance as needing `@retroactive` once, or not at all if one of those clauses is marked `@retroactive`. Resolves rdar://121479047
1 parent 4112618 commit a723538

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,7 @@ static void diagnoseRetroactiveConformances(
16411641
// At this point, we know we're extending a type declared outside this module.
16421642
// We better only be conforming it to protocols declared within this module.
16431643
llvm::SmallSetVector<ProtocolDecl *, 8> externalProtocols;
1644+
llvm::SmallSet<ProtocolDecl *, 8> protocolsWithRetroactiveAttr;
16441645
for (const InheritedEntry &entry : ext->getInherited().getEntries()) {
16451646
if (entry.getType().isNull() || !entry.getTypeRepr()) {
16461647
continue;
@@ -1665,7 +1666,6 @@ static void diagnoseRetroactiveConformances(
16651666
auto conformanceRef = ext->getParentModule()->lookupConformance(
16661667
extendedType, decl);
16671668
if (!conformanceRef.isConcrete()) {
1668-
16691669
return TypeWalker::Action::Continue;
16701670
}
16711671
auto conformance = conformanceRef.getConcrete();
@@ -1701,6 +1701,10 @@ static void diagnoseRetroactiveConformances(
17011701

17021702
// If it's marked @retroactive, no need to warn.
17031703
if (entry.isRetroactive()) {
1704+
// Note that we encountered this protocol through a conformance marked
1705+
// @retroactive in case multiple clauses cause the protocol to be
1706+
// inherited.
1707+
protocolsWithRetroactiveAttr.insert(decl);
17041708
return TypeWalker::Action::Continue;
17051709
}
17061710

@@ -1712,6 +1716,11 @@ static void diagnoseRetroactiveConformances(
17121716
});
17131717
}
17141718

1719+
// Remove protocols that are reachable through a @retroactive conformance.
1720+
for (auto *proto : protocolsWithRetroactiveAttr) {
1721+
externalProtocols.remove(proto);
1722+
}
1723+
17151724
// If we didn't find any violations, we're done.
17161725
if (externalProtocols.empty()) {
17171726
return;

test/Sema/extension_retroactive_conformances.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@
66
// Define a couple protocols with no requirements that're easy to conform to
77
public protocol SampleProtocol1 {}
88
public protocol SampleProtocol2 {}
9+
public protocol SampleProtocol1a: SampleProtocol1 {}
10+
public protocol SampleProtocol1b: SampleProtocol1 {}
911

1012
public struct Sample1 {}
1113
public struct Sample2 {}
14+
public struct Sample2a {}
15+
public struct Sample2b {}
16+
public struct Sample2c {}
17+
public struct Sample2d {}
18+
public struct Sample2e {}
1219
public struct Sample3 {}
1320
public struct Sample4 {}
1421
public struct Sample5 {}
@@ -36,6 +43,19 @@ extension Sample2: InheritsSampleProtocol {} // expected-warning {{extension dec
3643

3744
extension SampleAlreadyConforms: InheritsSampleProtocol {} // ok, SampleAlreadyConforms already conforms in the source module
3845

46+
extension Sample2a: @retroactive SampleProtocol1, InheritsSampleProtocol {} // ok, the concrete conformance to SampleProtocol1 has been declared retroactive
47+
48+
extension Sample2b: InheritsSampleProtocol, @retroactive SampleProtocol1 {} // ok, same as above but in the opposite order
49+
50+
// FIXME: It would be better to only suggest marking SampleProtocol1a @retroactive here
51+
extension Sample2c: SampleProtocol1a {} // expected-warning {{extension declares a conformance of imported type 'Sample2c' to imported protocols 'SampleProtocol1a', 'SampleProtocol1'}}
52+
// expected-note @-1 {{add '@retroactive' to silence this warning}} {{21-37=@retroactive SampleProtocol1a}} {{1-1=extension Sample2c: @retroactive SampleProtocol1 {\}\n}}
53+
54+
extension Sample2d: @retroactive SampleProtocol1a {} // ok, retroactive conformance to SampleProtocol1a covers conformance to SampleProtocol1
55+
56+
extension Sample2e: SampleProtocol1a, @retroactive SampleProtocol1b {} // expected-warning {{extension declares a conformance of imported type 'Sample2e' to imported protocol 'SampleProtocol1a'}}
57+
// expected-note @-1 {{add '@retroactive' to silence this warning}} {{21-37=@retroactive SampleProtocol1a}}
58+
3959
extension Sample3: NestedInheritsSampleProtocol {} // expected-warning {{extension declares a conformance of imported type 'Sample3' to imported protocol 'SampleProtocol1'}}
4060
// expected-note @-1 {{add '@retroactive' to silence this warning}} {{1-1=extension Sample3: @retroactive SampleProtocol1 {\}\n}}
4161

0 commit comments

Comments
 (0)