Skip to content

Commit 59bcbb3

Browse files
authored
Merge pull request #16201 from huonw/overlapping-conditional-conformances
[Sema] Diagnose redundant conditional conformances more specifically.
2 parents 7e68e8f + 2e5799e commit 59bcbb3

File tree

7 files changed

+116
-8
lines changed

7 files changed

+116
-8
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,10 +1697,19 @@ ERROR(witness_unavailable,none,
16971697

16981698
ERROR(redundant_conformance,none,
16991699
"redundant conformance of %0 to protocol %1", (Type, DeclName))
1700+
ERROR(redundant_conformance_conditional,none,
1701+
"conflicting conformance of %0 to protocol %1; there cannot be more "
1702+
"than one conformance, even with different conditional bounds",
1703+
(Type, DeclName))
17001704
WARNING(redundant_conformance_adhoc,none,
17011705
"conformance of %0 to protocol %1 was already stated in "
17021706
"%select{the protocol's|the type's}2 module %3",
17031707
(Type, DeclName, bool, Identifier))
1708+
WARNING(redundant_conformance_adhoc_conditional,none,
1709+
"conformance of %0 to protocol %1 conflicts with that stated in "
1710+
"%select{the protocol's|the type's}2 module %3 and will be ignored; "
1711+
"there cannot be more than one conformance, even with different conditional bounds",
1712+
(Type, DeclName, bool, Identifier))
17041713
NOTE(redundant_conformance_witness_ignored,none,
17051714
"%0 %1 will not be used to satisfy the conformance to %2",
17061715
(DescriptiveDeclKind, DeclName, DeclName))

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4603,6 +4603,12 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
46034603

46044604
// Complain about the redundant conformance.
46054605

4606+
auto currentSig = dc->getGenericSignatureOfContext();
4607+
auto existingSig = diag.ExistingDC->getGenericSignatureOfContext();
4608+
auto differentlyConditional = currentSig && existingSig &&
4609+
currentSig->getCanonicalSignature() !=
4610+
existingSig->getCanonicalSignature();
4611+
46064612
// If we've redundantly stated a conformance for which the original
46074613
// conformance came from the module of the type or the module of the
46084614
// protocol, just warn; we'll pick up the original conformance.
@@ -4614,11 +4620,13 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
46144620
extendedNominal->getParentModule()->getName() ||
46154621
existingModule == diag.Protocol->getParentModule())) {
46164622
// Warn about the conformance.
4617-
diagnose(diag.Loc, diag::redundant_conformance_adhoc,
4618-
dc->getDeclaredInterfaceType(),
4623+
auto diagID = differentlyConditional
4624+
? diag::redundant_conformance_adhoc_conditional
4625+
: diag::redundant_conformance_adhoc;
4626+
diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(),
46194627
diag.Protocol->getName(),
46204628
existingModule->getName() ==
4621-
extendedNominal->getParentModule()->getName(),
4629+
extendedNominal->getParentModule()->getName(),
46224630
existingModule->getName());
46234631

46244632
// Complain about any declarations in this extension whose names match
@@ -4649,8 +4657,10 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
46494657
}
46504658
}
46514659
} else {
4652-
diagnose(diag.Loc, diag::redundant_conformance,
4653-
dc->getDeclaredInterfaceType(),
4660+
auto diagID = differentlyConditional
4661+
? diag::redundant_conformance_conditional
4662+
: diag::redundant_conformance;
4663+
diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(),
46544664
diag.Protocol->getName());
46554665
}
46564666

test/Generics/conditional_conformances.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,13 @@ struct TwoConformances<T> {}
322322
extension TwoConformances: P2 where T: P1 {}
323323
// expected-note@-1{{'TwoConformances<T>' declares conformance to protocol 'P2' here}}
324324
extension TwoConformances: P2 where T: P3 {}
325-
// expected-error@-1{{redundant conformance of 'TwoConformances<T>' to protocol 'P2'}}
325+
// expected-error@-1{{conflicting conformance of 'TwoConformances<T>' to protocol 'P2'; there cannot be more than one conformance, even with different conditional bounds}}
326326

327327
struct TwoDisjointConformances<T> {}
328328
extension TwoDisjointConformances: P2 where T == Int {}
329329
// expected-note@-1{{'TwoDisjointConformances<T>' declares conformance to protocol 'P2' here}}
330330
extension TwoDisjointConformances: P2 where T == String {}
331-
// expected-error@-1{{redundant conformance of 'TwoDisjointConformances<T>' to protocol 'P2'}}
331+
// expected-error@-1{{conflicting conformance of 'TwoDisjointConformances<T>' to protocol 'P2'; there cannot be more than one conformance, even with different conditional bounds}}
332332

333333

334334
// FIXME: these cases should be equivalent (and both with the same output as the

test/decl/protocol/conforms/Inputs/redundant_conformance_A.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,22 @@ public struct OtherConformsToP {
1212
public func f() -> Int { return 0 }
1313
}
1414

15+
// The downstream conformance is conditional
16+
public struct GenericConformsToP<T> : P1 {
17+
public func f() -> Int { return 0 }
18+
}
19+
20+
public struct OtherGenericConformsToP<T> {
21+
public func f() -> Int { return 0 }
22+
}
23+
24+
// The upstream conformance is conditional
25+
public struct GenericConditionalConformsToP<T> {}
26+
extension GenericConditionalConformsToP: P1 where T == Int {
27+
public typealias A = Int
28+
public func f() -> Int { return 0 }
29+
}
30+
31+
public struct OtherGenericConditionalConformsToP<T> {
32+
public func f() -> Int { return 0 }
33+
}

test/decl/protocol/conforms/Inputs/redundant_conformance_B.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,16 @@ public protocol P2 {
88

99
extension OtherConformsToP: P1 {
1010
}
11+
extension OtherGenericConformsToP: P1 {}
12+
extension OtherGenericConditionalConformsToP: P1 where T: P2 {}
1113

1214
extension ConformsToP: P2 {
1315
public func f() -> Int { return 0 }
1416
}
17+
18+
extension GenericConformsToP: P2 {
19+
public func f() -> Int { return 0 }
20+
}
21+
extension GenericConditionalConformsToP: P2 where T: P1 {
22+
public func f() -> Int { return 0 }
23+
}

test/decl/protocol/conforms/redundant_conformance.swift

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/redundant_conformance_A.swift
44
// RUN: %target-swift-frontend -emit-module -o %t -I %t %S/Inputs/redundant_conformance_B.swift
5-
// RUN: %target-typecheck-verify-swift -I %t %s
5+
// RUN: %target-typecheck-verify-swift -I %t
66

77
import redundant_conformance_A
88
import redundant_conformance_B
@@ -29,3 +29,39 @@ func testConformsToP(cp1: ConformsToP, ocp1: OtherConformsToP) {
2929

3030
let _ = ocp1.f() // okay: picks "our" OtherConformsToP.f()
3131
}
32+
33+
// slightly different error messages for conditional conformances:
34+
35+
extension GenericConformsToP: P1 where T: P1 {
36+
// expected-warning@-1{{conformance of 'GenericConformsToP<T>' to protocol 'P1' conflicts with that stated in the type's module 'redundant_conformance_A' and will be ignored; there cannot be more than one conformance, even with different conditional bounds}}
37+
typealias A = Double
38+
// expected-note@-1{{type alias 'A' will not be used to satisfy the conformance to 'P1'}}
39+
func f() -> Double { return 0.0 }
40+
// expected-note@-1{{instance method 'f()' will not be used to satisfy the conformance to 'P1'}}
41+
}
42+
extension GenericConformsToP: P2 where T: P1 {
43+
// expected-warning@-1{{conformance of 'GenericConformsToP<T>' to protocol 'P2' conflicts with that stated in the protocol's module 'redundant_conformance_B' and will be ignored; there cannot be more than one conformance, even with different conditional bounds}}
44+
}
45+
46+
extension OtherGenericConformsToP: P1 where T: P1 {
47+
// expected-error@-1{{conflicting conformance of 'OtherGenericConformsToP<T>' to protocol 'P1'; there cannot be more than one conformance, even with different conditional bounds}}
48+
typealias A = Double
49+
func f() -> Double { return 0.0 }
50+
}
51+
52+
extension GenericConditionalConformsToP: P1 {
53+
// expected-warning@-1{{conformance of 'GenericConditionalConformsToP<T>' to protocol 'P1' conflicts with that stated in the type's module 'redundant_conformance_A' and will be ignored; there cannot be more than one conformance, even with different conditional bounds}}
54+
typealias A = Double
55+
// expected-note@-1{{type alias 'A' will not be used to satisfy the conformance to 'P1'}}
56+
func f() -> Double { return 0.0 }
57+
// expected-note@-1{{instance method 'f()' will not be used to satisfy the conformance to 'P1'}}
58+
}
59+
extension GenericConditionalConformsToP: P2 {
60+
// expected-warning@-1{{conformance of 'GenericConditionalConformsToP<T>' to protocol 'P2' conflicts with that stated in the protocol's module 'redundant_conformance_B' and will be ignored; there cannot be more than one conformance, even with different conditional bounds}}
61+
}
62+
63+
extension OtherGenericConditionalConformsToP: P1 {
64+
// expected-error@-1{{conflicting conformance of 'OtherGenericConditionalConformsToP<T>' to protocol 'P1'; there cannot be more than one conformance, even with different conditional bounds}}
65+
typealias A = Double
66+
func f() -> Double { return 0.0 }
67+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/redundant_conformance_A.swift
4+
// RUN: %target-swift-frontend -emit-module -o %t -I %t %S/Inputs/redundant_conformance_B.swift
5+
// RUN: %target-typecheck-verify-swift -I %t
6+
7+
import redundant_conformance_A
8+
import redundant_conformance_B
9+
10+
// These have identical requirements to the original conformances
11+
12+
extension GenericConditionalConformsToP: P1 where T == Int {
13+
// expected-warning@-1{{conformance of 'GenericConditionalConformsToP<T>' to protocol 'P1' was already stated in the type's module 'redundant_conformance_A'}}
14+
func f() -> Int { return 0 }
15+
// expected-note@-1{{instance method 'f()' will not be used to satisfy the conformance to 'P1'}}
16+
}
17+
extension GenericConditionalConformsToP: P2 where T: P1 {
18+
// expected-warning@-1{{conformance of 'GenericConditionalConformsToP<T>' to protocol 'P2' was already stated in the protocol's module 'redundant_conformance_B'}}
19+
func f() -> Int { return 0 }
20+
// expected-note@-1{{instance method 'f()' will not be used to satisfy the conformance to 'P2'}}
21+
}
22+
extension OtherGenericConditionalConformsToP: P1 where T: P2 {
23+
// expected-error@-1{{redundant conformance of 'OtherGenericConditionalConformsToP<T>' to protocol 'P1'}}
24+
func f() -> Int { return 0 }
25+
}

0 commit comments

Comments
 (0)