Skip to content

Commit 37de124

Browse files
authored
Merge pull request #9160 from DougGregor/redundant-conformance-warnings-4.0
[Type checker] Downgrade some "redundant conformance" errors to warnings.
2 parents 2bfc7b3 + 52804dd commit 37de124

File tree

9 files changed

+151
-18
lines changed

9 files changed

+151
-18
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,13 @@ WARNING(witness_unavailable,none,
15421542

15431543
ERROR(redundant_conformance,none,
15441544
"redundant conformance of %0 to protocol %1", (Type, DeclName))
1545+
WARNING(redundant_conformance_adhoc,none,
1546+
"conformance of %0 to protocol %1 was already stated in "
1547+
"%select{the protocol's|the type's}2 module %3",
1548+
(Type, DeclName, bool, Identifier))
1549+
NOTE(redundant_conformance_witness_ignored,none,
1550+
"%0 %1 will not be used to satisfy the conformance to %2",
1551+
(DescriptiveDeclKind, DeclName, DeclName))
15451552

15461553
// "Near matches"
15471554
WARNING(optional_req_near_match,none,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5884,10 +5884,56 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
58845884
if (!existingDecl)
58855885
existingDecl = cast<ExtensionDecl>(diag.ExistingDC);
58865886

5887-
// Complain about redundant conformances.
5888-
diagnose(diag.Loc, diag::redundant_conformance,
5889-
dc->getDeclaredInterfaceType(),
5890-
diag.Protocol->getName());
5887+
// Complain about the redundant conformance.
5888+
5889+
// If we've redundantly stated a conformance for which the original
5890+
// conformance came from the module of the type or the module of the
5891+
// protocol, just warn; we'll pick up the original conformance.
5892+
auto existingModule = diag.ExistingDC->getParentModule();
5893+
auto extendedNominal =
5894+
diag.ExistingDC->getAsNominalTypeOrNominalTypeExtensionContext();
5895+
if (existingModule != dc->getParentModule() &&
5896+
(existingModule == extendedNominal->getParentModule() ||
5897+
existingModule == diag.Protocol->getParentModule())) {
5898+
// Warn about the conformance.
5899+
diagnose(diag.Loc, diag::redundant_conformance_adhoc,
5900+
dc->getDeclaredInterfaceType(),
5901+
diag.Protocol->getName(),
5902+
existingModule == extendedNominal->getParentModule(),
5903+
existingModule->getName());
5904+
5905+
// Complain about any declarations in this extension whose names match
5906+
// a requirement in that protocol.
5907+
SmallPtrSet<DeclName, 4> diagnosedNames;
5908+
for (auto decl : idc->getMembers()) {
5909+
if (decl->isImplicit())
5910+
continue;
5911+
5912+
auto value = dyn_cast<ValueDecl>(decl);
5913+
if (!value) continue;
5914+
5915+
if (!diagnosedNames.insert(value->getFullName()).second)
5916+
continue;
5917+
5918+
bool valueIsType = isa<TypeDecl>(value);
5919+
for (auto requirement
5920+
: diag.Protocol->lookupDirect(value->getFullName(),
5921+
/*ignoreNewExtensions=*/true)) {
5922+
auto requirementIsType = isa<TypeDecl>(requirement);
5923+
if (valueIsType != requirementIsType)
5924+
continue;
5925+
5926+
diagnose(value, diag::redundant_conformance_witness_ignored,
5927+
value->getDescriptiveKind(), value->getFullName(),
5928+
diag.Protocol->getFullName());
5929+
break;
5930+
}
5931+
}
5932+
} else {
5933+
diagnose(diag.Loc, diag::redundant_conformance,
5934+
dc->getDeclaredInterfaceType(),
5935+
diag.Protocol->getName());
5936+
}
58915937

58925938
// Special case: explain that 'RawRepresentable' conformance
58935939
// is implied for enums which already declare a raw type.

lib/Serialization/Deserialization.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,11 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule,
11281128

11291129
auto newEnd = std::remove_if(values.begin(), values.end(),
11301130
[=](ValueDecl *value) {
1131+
// Ignore anything that was parsed (vs. deserialized), because a serialized
1132+
// module cannot refer to it.
1133+
if (value->getDeclContext()->getParentSourceFile())
1134+
return true;
1135+
11311136
if (isType != isa<TypeDecl>(value))
11321137
return true;
11331138
if (!value->hasInterfaceType())
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 3
2+
3+
extension String : Collection { // expected-warning{{conformance of 'String' to protocol 'Collection' was already stated in the type's module 'Swift'}}
4+
subscript (i: Index) -> String { // expected-note{{subscript 'subscript' will not be used to satisfy the conformance to 'Collection'}}
5+
get { return self }
6+
set { }
7+
}
8+
9+
var isEmpty: Bool { return false } // expected-note{{var 'isEmpty' will not be used to satisfy the conformance to 'Collection'}}
10+
11+
}
12+
13+
func testStringOps(s: String) {
14+
_ = s.isEmpty
15+
}

test/Sema/enum_post_hoc_raw_representable_with_raw_type.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55

66
import enum_with_raw_type
77

8-
// expected-error@+1{{redundant conformance of 'Foo' to protocol 'RawRepresentable'}}
8+
// expected-warning@+1{{conformance of 'Foo' to protocol 'RawRepresentable' was already stated in the type's module 'enum_with_raw_type'}}
99
extension Foo: RawRepresentable {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
public protocol P1 {
2+
associatedtype A
3+
4+
func f() -> A
5+
}
6+
7+
public struct ConformsToP : P1 {
8+
public func f() -> Int { return 0 }
9+
}
10+
11+
public struct OtherConformsToP {
12+
public func f() -> Int { return 0 }
13+
}
14+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import redundant_conformance_A
2+
3+
public protocol P2 {
4+
associatedtype A
5+
6+
func f() -> A
7+
}
8+
9+
extension OtherConformsToP: P1 {
10+
}
11+
12+
extension ConformsToP: P2 {
13+
public func f() -> Int { return 0 }
14+
}

test/decl/protocol/conforms/placement.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -189,31 +189,31 @@ enum MFSynthesizedEnum2 : Int { case none = 0 }
189189
// ===========================================================================
190190
// Tests with conformances in imported modules
191191
// ===========================================================================
192-
extension MMExplicit1 : MMP1 { } // expected-error{{redundant conformance of 'MMExplicit1' to protocol 'MMP1'}}
192+
extension MMExplicit1 : MMP1 { } // expected-warning{{conformance of 'MMExplicit1' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}}
193193

194-
extension MMExplicit1 : MMP2a { } // expected-error{{redundant conformance of 'MMExplicit1' to protocol 'MMP2a'}}
195-
extension MMExplicit1 : MMP3a { } // expected-error{{redundant conformance of 'MMExplicit1' to protocol 'MMP3a'}}
194+
extension MMExplicit1 : MMP2a { } // expected-warning{{MMExplicit1' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A}}
195+
extension MMExplicit1 : MMP3a { } // expected-warning{{conformance of 'MMExplicit1' to protocol 'MMP3a' was already stated in the type's module 'placement_module_A'}}
196196

197197
extension MMExplicit1 : MMP3b { } // okay
198198

199-
extension MMSuper1 : MMP1 { } // expected-error{{redundant conformance of 'MMSuper1' to protocol 'MMP1'}}
200-
extension MMSuper1 : MMP2a { } // expected-error{{redundant conformance of 'MMSuper1' to protocol 'MMP2a'}}
199+
extension MMSuper1 : MMP1 { } // expected-warning{{conformance of 'MMSuper1' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}}
200+
extension MMSuper1 : MMP2a { } // expected-warning{{conformance of 'MMSuper1' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}}
201201
extension MMSuper1 : MMP3b { } // okay
202202

203-
extension MMSub1 : AnyObject { } // expected-error{{redundant conformance of 'MMSub1' to protocol 'AnyObject'}}
203+
extension MMSub1 : AnyObject { } // expected-warning{{conformance of 'MMSub1' to protocol 'AnyObject' was already stated in the type's module 'placement_module_A'}}
204204

205-
extension MMSub2 : MMP1 { } // expected-error{{redundant conformance of 'MMSub2' to protocol 'MMP1'}}
206-
extension MMSub2 : MMP2a { } // expected-error{{redundant conformance of 'MMSub2' to protocol 'MMP2a'}}
205+
extension MMSub2 : MMP1 { } // expected-warning{{conformance of 'MMSub2' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}}
206+
extension MMSub2 : MMP2a { } // expected-warning{{conformance of 'MMSub2' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}}
207207
extension MMSub2 : MMP3b { } // okay
208208

209209
extension MMSub2 : MMAnyObjectRefinement { } // okay
210210

211-
extension MMSub3 : MMP1 { } // expected-error{{redundant conformance of 'MMSub3' to protocol 'MMP1'}}
212-
extension MMSub3 : MMP2a { } // expected-error{{redundant conformance of 'MMSub3' to protocol 'MMP2a'}}
211+
extension MMSub3 : MMP1 { } // expected-warning{{conformance of 'MMSub3' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}}
212+
extension MMSub3 : MMP2a { } // expected-warning{{conformance of 'MMSub3' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}}
213213
extension MMSub3 : MMP3b { } // okay
214-
extension MMSub3 : AnyObject { } // expected-error{{redundant conformance of 'MMSub3' to protocol 'AnyObject'}}
214+
extension MMSub3 : AnyObject { } // expected-warning{{conformance of 'MMSub3' to protocol 'AnyObject' was already stated in the type's module 'placement_module_A'}}
215215

216-
extension MMSub4 : MMP1 { } // expected-error{{redundant conformance of 'MMSub4' to protocol 'MMP1'}}
217-
extension MMSub4 : MMP2a { } // expected-error{{redundant conformance of 'MMSub4' to protocol 'MMP2a'}}
216+
extension MMSub4 : MMP1 { } // expected-warning{{conformance of 'MMSub4' to protocol 'MMP1' was already stated in the type's module 'placement_module_B'}}
217+
extension MMSub4 : MMP2a { } // expected-warning{{conformance of 'MMSub4' to protocol 'MMP2a' was already stated in the type's module 'placement_module_B'}}
218218
extension MMSub4 : MMP3b { } // okay
219219
extension MMSub4 : AnyObjectRefinement { } // okay
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
4+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/redundant_conformance_A.swift
5+
// RUN: %target-swift-frontend -emit-module -o %t -I %t %S/Inputs/redundant_conformance_B.swift
6+
// RUN: %target-typecheck-verify-swift -I %t %s
7+
8+
import redundant_conformance_A
9+
import redundant_conformance_B
10+
11+
extension ConformsToP
12+
: P1 { // expected-warning{{conformance of 'ConformsToP' to protocol 'P1' was already stated in the type's module 'redundant_conformance_A'}}
13+
typealias A = Double // expected-note{{type alias 'A' will not be used to satisfy the conformance to 'P1'}}
14+
15+
func f() -> Double { return 0.0 } // expected-note{{instance method 'f()' will not be used to satisfy the conformance to 'P1'}}
16+
// expected-note@-1{{found this candidate}}
17+
}
18+
19+
extension ConformsToP
20+
: P2 { // expected-warning{{conformance of 'ConformsToP' to protocol 'P2' was already stated in the protocol's module 'redundant_conformance_B'}}
21+
}
22+
23+
extension OtherConformsToP : P1 { // expected-error{{redundant conformance of 'OtherConformsToP' to protocol 'P1'}}
24+
func f() -> Int { return 0 }
25+
}
26+
27+
func testConformsToP(cp1: ConformsToP, ocp1: OtherConformsToP) {
28+
// Note:
29+
let _ = cp1.f() // expected-error{{ambiguous use of 'f()'}}
30+
31+
let _ = ocp1.f() // okay: picks "our" OtherConformsToP.f()
32+
}

0 commit comments

Comments
 (0)