Skip to content

Commit cc83510

Browse files
committed
[Sema] Diagnose deprecated default implementations in the witness checker.
If a protocol provides a deprecated default implementation for a requirement that is not deprecated, the compiler should emit a warning so the programmer can provide an explicit implementation of the requirement. This is helpful for staging in new protocol requirements that should be implemented in conforming types. (cherry picked from commit 005b45c)
1 parent df0f784 commit cc83510

File tree

5 files changed

+113
-5
lines changed

5 files changed

+113
-5
lines changed

include/swift/AST/Attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,10 @@ class DeclAttributes {
26932693
return getUnavailable(ctx) != nullptr;
26942694
}
26952695

2696+
bool isDeprecated(const ASTContext &ctx) const {
2697+
return getDeprecated(ctx) != nullptr;
2698+
}
2699+
26962700
/// Determine whether there is a swiftVersionSpecific attribute that's
26972701
/// unavailable relative to the provided language version.
26982702
bool

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3119,6 +3119,11 @@ ERROR(witness_unavailable,none,
31193119
"unavailable %kind0 was used to satisfy a requirement of protocol %1%select{|: %2}2",
31203120
(const ValueDecl *, Identifier, StringRef))
31213121

3122+
WARNING(witness_deprecated,none,
3123+
"deprecated default implementation is used to satisfy %kind0 required by "
3124+
"protocol %1%select{|: %2}2",
3125+
(const ValueDecl *, Identifier, StringRef))
3126+
31223127
ERROR(redundant_conformance,none,
31233128
"redundant conformance of %0 to protocol %1", (Type, Identifier))
31243129
ERROR(redundant_conformance_conditional,none,

include/swift/AST/RequirementMatch.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ enum class CheckKind : unsigned {
239239

240240
/// The witness itself is inaccessible.
241241
WitnessUnavailable,
242+
243+
/// The witness is a deprecated default implementation provided by the
244+
/// protocol.
245+
DefaultWitnessDeprecated,
242246
};
243247
/// Describes the suitability of the chosen witness for
244248
/// the requirement.
@@ -464,4 +468,4 @@ struct RequirementMatch {
464468

465469
}
466470

467-
#endif // SWIFT_AST_REQUIREMENTMATCH_H
471+
#endif // SWIFT_AST_REQUIREMENTMATCH_H

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,8 @@ checkWitnessAvailability(ValueDecl *requirement,
17401740

17411741
RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
17421742
const RequirementMatch &match) {
1743+
auto &ctx = getASTContext();
1744+
17431745
if (!match.OptionalAdjustments.empty())
17441746
return CheckKind::OptionalityConflict;
17451747

@@ -1769,7 +1771,7 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
17691771
return RequirementCheck(CheckKind::Availability, requiredAvailability);
17701772
}
17711773

1772-
if (requirement->getAttrs().isUnavailable(getASTContext()) &&
1774+
if (requirement->getAttrs().isUnavailable(ctx) &&
17731775
match.Witness->getDeclContext() == DC) {
17741776
return RequirementCheck(CheckKind::Unavailable);
17751777
}
@@ -1792,11 +1794,11 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
17921794
}
17931795
}
17941796

1795-
if (match.Witness->getAttrs().isUnavailable(getASTContext()) &&
1796-
!requirement->getAttrs().isUnavailable(getASTContext())) {
1797+
if (match.Witness->getAttrs().isUnavailable(ctx) &&
1798+
!requirement->getAttrs().isUnavailable(ctx)) {
17971799
auto nominalOrExtensionIsUnavailable = [&]() {
17981800
if (auto extension = dyn_cast<ExtensionDecl>(DC)) {
1799-
if (extension->getAttrs().isUnavailable(getASTContext()))
1801+
if (extension->getAttrs().isUnavailable(ctx))
18001802
return true;
18011803
}
18021804

@@ -1813,6 +1815,20 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
18131815
return CheckKind::WitnessUnavailable;
18141816
}
18151817

1818+
// Warn about deprecated default implementations if the requirement is
1819+
// not deprecated, and the conformance is not deprecated.
1820+
bool isDefaultWitness = false;
1821+
if (auto *nominal = match.Witness->getDeclContext()->getSelfNominalTypeDecl())
1822+
isDefaultWitness = isa<ProtocolDecl>(nominal);
1823+
if (isDefaultWitness &&
1824+
match.Witness->getAttrs().isDeprecated(ctx) &&
1825+
!requirement->getAttrs().isDeprecated(ctx)) {
1826+
auto conformanceContext = ExportContext::forConformance(DC, Proto);
1827+
if (!conformanceContext.isDeprecated()) {
1828+
return RequirementCheck(CheckKind::DefaultWitnessDeprecated);
1829+
}
1830+
}
1831+
18161832
return CheckKind::Success;
18171833
}
18181834

@@ -4374,6 +4390,25 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
43744390
requirement->getName());
43754391
});
43764392
break;
4393+
4394+
case CheckKind::DefaultWitnessDeprecated:
4395+
getASTContext().addDelayedConformanceDiag(
4396+
Conformance, /*isError=*/false,
4397+
[witness, requirement](NormalProtocolConformance *conformance) {
4398+
auto &ctx = witness->getASTContext();
4399+
auto &diags = ctx.Diags;
4400+
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
4401+
auto *attr = witness->getAttrs().getDeprecated(ctx);
4402+
EncodedDiagnosticMessage EncodedMessage(attr->Message);
4403+
diags.diagnose(diagLoc, diag::witness_deprecated,
4404+
witness, conformance->getProtocol()->getName(),
4405+
EncodedMessage.Message);
4406+
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
4407+
diags.diagnose(requirement, diag::kind_declname_declared_here,
4408+
DescriptiveDeclKind::Requirement,
4409+
requirement->getName());
4410+
});
4411+
break;
43774412
}
43784413

43794414
if (auto *classDecl = DC->getSelfClassDecl()) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol DeprecatedRequirement {
4+
@available(*, deprecated)
5+
func f()
6+
}
7+
8+
extension DeprecatedRequirement {
9+
@available(*, deprecated)
10+
func f() {}
11+
}
12+
13+
// No warning if both the requirement and the default implementation are deprecated
14+
struct S1: DeprecatedRequirement {}
15+
16+
protocol DeprecatedDefault {
17+
func f() // expected-note {{requirement 'f()' declared here}}
18+
}
19+
20+
extension DeprecatedDefault {
21+
@available(*, deprecated)
22+
func f() {} // expected-note {{'f()' declared here}}
23+
}
24+
25+
// expected-warning@+1 {{deprecated default implementation is used to satisfy instance method 'f()' required by protocol 'DeprecatedDefault'}}
26+
struct S2: DeprecatedDefault {}
27+
28+
// No warning if the conformance itself is deprecated
29+
@available(*, deprecated)
30+
struct S3: DeprecatedDefault {
31+
}
32+
33+
struct S4: DeprecatedDefault {
34+
func f() {}
35+
}
36+
37+
struct S5 {}
38+
39+
// No warning if the conformance itself is deprecated
40+
@available(*, deprecated)
41+
extension S5: DeprecatedDefault {}
42+
43+
@available(*, deprecated)
44+
enum UnavailableEnum {
45+
struct Nested: DeprecatedDefault {}
46+
}
47+
48+
// Include message string from @available attribute if provided
49+
protocol DeprecatedDefaultWithMessage {
50+
func f() // expected-note {{requirement 'f()' declared here}}
51+
}
52+
53+
extension DeprecatedDefaultWithMessage {
54+
@available(*, deprecated, message: "write it yourself")
55+
func f() {} // expected-note {{'f()' declared here}}
56+
}
57+
58+
59+
// expected-warning@+1 {{deprecated default implementation is used to satisfy instance method 'f()' required by protocol 'DeprecatedDefaultWithMessage': write it yourself}}
60+
struct S6: DeprecatedDefaultWithMessage {}

0 commit comments

Comments
 (0)