Skip to content

Commit 033a38e

Browse files
authored
Merge pull request #9830 from DougGregor/same-type-self-soundness-hole
2 parents 0c9ab7b + f0abcac commit 033a38e

File tree

6 files changed

+178
-10
lines changed

6 files changed

+178
-10
lines changed

include/swift/AST/Decl.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3418,46 +3418,54 @@ class ClassDecl : public NominalTypeDecl {
34183418
struct SelfReferenceKind {
34193419
bool result;
34203420
bool parameter;
3421+
bool requirement;
34213422
bool other;
34223423

34233424
/// The type does not refer to 'Self' at all.
34243425
static SelfReferenceKind None() {
3425-
return SelfReferenceKind(false, false, false);
3426+
return SelfReferenceKind(false, false, false, false);
34263427
}
34273428

34283429
/// The type refers to 'Self', but only as the result type of a method.
34293430
static SelfReferenceKind Result() {
3430-
return SelfReferenceKind(true, false, false);
3431+
return SelfReferenceKind(true, false, false, false);
34313432
}
34323433

34333434
/// The type refers to 'Self', but only as the parameter type of a method.
34343435
static SelfReferenceKind Parameter() {
3435-
return SelfReferenceKind(false, true, false);
3436+
return SelfReferenceKind(false, true, false, false);
3437+
}
3438+
3439+
/// The type refers to 'Self' within a same-type requiement.
3440+
static SelfReferenceKind Requirement() {
3441+
return SelfReferenceKind(false, false, true, false);
34363442
}
34373443

34383444
/// The type refers to 'Self' in a position that is invariant.
34393445
static SelfReferenceKind Other() {
3440-
return SelfReferenceKind(false, false, true);
3446+
return SelfReferenceKind(false, false, false, true);
34413447
}
34423448

34433449
SelfReferenceKind flip() const {
3444-
return SelfReferenceKind(parameter, result, other);
3450+
return SelfReferenceKind(parameter, result, requirement, other);
34453451
}
34463452

34473453
SelfReferenceKind operator|=(SelfReferenceKind kind) {
34483454
result |= kind.result;
3455+
requirement |= kind.requirement;
34493456
parameter |= kind.parameter;
34503457
other |= kind.other;
34513458
return *this;
34523459
}
34533460

34543461
operator bool() const {
3455-
return result || parameter || other;
3462+
return result || parameter || requirement || other;
34563463
}
34573464

34583465
private:
3459-
SelfReferenceKind(bool result, bool parameter, bool other)
3460-
: result(result), parameter(parameter), other(other) { }
3466+
SelfReferenceKind(bool result, bool parameter, bool requirement, bool other)
3467+
: result(result), parameter(parameter), requirement(requirement),
3468+
other(other) { }
34613469
};
34623470

34633471
/// ProtocolDecl - A declaration of a protocol, for example:

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,19 @@ ERROR(witness_self_non_subtype,none,
14131413
"(%2) because it uses 'Self' in a non-parameter, non-result type "
14141414
"position",
14151415
(Type, DeclName, Type))
1416+
ERROR(witness_self_same_type,none,
1417+
"%0 %1 in non-final class %2 cannot be used to satisfy requirement %3 %4"
1418+
" (in protocol %5) due to same-type requirement involving 'Self'",
1419+
(DescriptiveDeclKind, DeclName, Type, DescriptiveDeclKind,
1420+
DeclName, Type))
1421+
WARNING(witness_self_same_type_warn,none,
1422+
"%0 %1 in non-final class %2 cannot be used to satisfy requirement %3 %4"
1423+
" (in protocol %5) due to same-type requirement involving 'Self'",
1424+
(DescriptiveDeclKind, DeclName, Type, DescriptiveDeclKind,
1425+
DeclName, Type))
1426+
NOTE(witness_self_weaken_same_type,none,
1427+
"consider weakening the same-type requirement %0 == %1 to a superclass "
1428+
"requirement", (Type, Type))
14161429
ERROR(witness_requires_dynamic_self,none,
14171430
"method %0 in non-final class %1 must return `Self` to conform to "
14181431
"protocol %2",

lib/AST/Decl.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3043,6 +3043,25 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
30433043
return SelfReferenceKind::None();
30443044
}
30453045

3046+
/// Find Self references in a generic signature's same-type requirements.
3047+
static SelfReferenceKind
3048+
findProtocolSelfReferences(const ProtocolDecl *protocol,
3049+
GenericSignature *genericSig){
3050+
if (!genericSig) return SelfReferenceKind::None();
3051+
3052+
auto selfTy = protocol->getSelfInterfaceType();
3053+
for (const auto &req : genericSig->getRequirements()) {
3054+
if (req.getKind() != RequirementKind::SameType)
3055+
continue;
3056+
3057+
if (req.getFirstType()->isEqual(selfTy) ||
3058+
req.getSecondType()->isEqual(selfTy))
3059+
return SelfReferenceKind::Requirement();
3060+
}
3061+
3062+
return SelfReferenceKind::None();
3063+
}
3064+
30463065
/// Find Self references within the given requirement.
30473066
SelfReferenceKind
30483067
ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value,
@@ -3062,7 +3081,7 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value,
30623081
if (type->hasError())
30633082
return SelfReferenceKind::None();
30643083

3065-
if (isa<AbstractFunctionDecl>(value)) {
3084+
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
30663085
// Skip the 'self' parameter.
30673086
type = type->castTo<AnyFunctionType>()->getResult();
30683087

@@ -3076,9 +3095,24 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value,
30763095
return SelfReferenceKind::Other();
30773096
}
30783097

3098+
// Check the requirements of a generic function.
3099+
if (func->isGeneric()) {
3100+
if (auto result =
3101+
::findProtocolSelfReferences(this, func->getGenericSignature()))
3102+
return result;
3103+
}
3104+
30793105
return ::findProtocolSelfReferences(this, type,
30803106
skipAssocTypes);
3081-
} else if (isa<SubscriptDecl>(value)) {
3107+
} else if (auto subscript = dyn_cast<SubscriptDecl>(value)) {
3108+
// Check the requirements of a generic subscript.
3109+
if (subscript->isGeneric()) {
3110+
if (auto result =
3111+
::findProtocolSelfReferences(this,
3112+
subscript->getGenericSignature()))
3113+
return result;
3114+
}
3115+
30823116
return ::findProtocolSelfReferences(this, type,
30833117
skipAssocTypes);
30843118
} else {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2794,6 +2794,50 @@ witnessHasImplementsAttrForRequirement(ValueDecl *witness,
27942794
return false;
27952795
}
27962796

2797+
/// Determine the given witness has a same-type constraint constraining the
2798+
/// given 'Self' type, and return the
2799+
///
2800+
/// \returns None if there is no such constraint; a non-empty optional that
2801+
/// may have the \c RequirementRepr for the actual constraint.
2802+
static Optional<RequirementRepr *>
2803+
getAdopteeSelfSameTypeConstraint(ClassDecl *selfClass, ValueDecl *witness) {
2804+
auto genericSig =
2805+
witness->getInnermostDeclContext()->getGenericSignatureOfContext();
2806+
if (!genericSig) return None;
2807+
2808+
for (const auto &req : genericSig->getRequirements()) {
2809+
if (req.getKind() != RequirementKind::SameType)
2810+
continue;
2811+
2812+
if (req.getFirstType()->getAnyNominal() == selfClass ||
2813+
req.getSecondType()->getAnyNominal() == selfClass) {
2814+
// Try to find the requirement-as-written.
2815+
GenericParamList *genericParams = nullptr;
2816+
2817+
if (auto func = dyn_cast<AbstractFunctionDecl>(witness))
2818+
genericParams = func->getGenericParams();
2819+
else if (auto subscript = dyn_cast<SubscriptDecl>(witness))
2820+
genericParams = subscript->getGenericParams();
2821+
if (genericParams) {
2822+
for (auto &req : genericParams->getRequirements()) {
2823+
if (req.getKind() != RequirementReprKind::SameType)
2824+
continue;
2825+
2826+
if (req.getFirstType()->getAnyNominal() == selfClass ||
2827+
req.getSecondType()->getAnyNominal() == selfClass)
2828+
return &req;
2829+
}
2830+
}
2831+
2832+
// Form an optional(nullptr) to indicate that we don't have the
2833+
// requirement itself.
2834+
return nullptr;
2835+
}
2836+
}
2837+
2838+
return None;
2839+
}
2840+
27972841
ResolveWitnessResult
27982842
ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
27992843
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
@@ -3076,6 +3120,31 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
30763120
conformance->getType());
30773121
});
30783122
}
3123+
} else if (selfKind.requirement) {
3124+
if (auto constraint = getAdopteeSelfSameTypeConstraint(classDecl,
3125+
witness)) {
3126+
// A "Self ==" constraint works incorrectly with subclasses. Complain.
3127+
auto proto = Conformance->getProtocol();
3128+
auto &diags = proto->getASTContext().Diags;
3129+
diags.diagnose(witness->getLoc(),
3130+
proto->getASTContext().LangOpts.isSwiftVersion3()
3131+
? diag::witness_self_same_type_warn
3132+
: diag::witness_self_same_type,
3133+
witness->getDescriptiveKind(),
3134+
witness->getFullName(),
3135+
Conformance->getType(),
3136+
requirement->getDescriptiveKind(),
3137+
requirement->getFullName(),
3138+
proto->getDeclaredType());
3139+
3140+
if (auto requirementRepr = *constraint) {
3141+
diags.diagnose(requirementRepr->getEqualLoc(),
3142+
diag::witness_self_weaken_same_type,
3143+
requirementRepr->getFirstType(),
3144+
requirementRepr->getSecondType())
3145+
.fixItReplace(requirementRepr->getEqualLoc(), ":");
3146+
}
3147+
}
30793148
}
30803149

30813150
// A non-final class can model a protocol requirement with a
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 3
2+
3+
protocol P {
4+
associatedtype T
5+
}
6+
7+
protocol Q {
8+
func foo<T: P>(_: T, _: T.T) where T.T == Self
9+
}
10+
11+
class C1: Q {
12+
func foo<T: P>(_: T, _: C1) where T.T == C1 {} // expected-warning{{instance method 'foo' in non-final class 'C1' cannot be used to satisfy requirement instance method 'foo' (in protocol 'Q') due to same-type requirement involving 'Self'}}}}
13+
// expected-note@-1{{consider weakening the same-type requirement 'T.T' == 'C1' to a superclass requirement}}{{41-43=:}}
14+
}
15+
16+
final class C2: Q {
17+
func foo<T: P>(_: T, _: C2) where T.T == C2 {}
18+
}
19+
20+
class C3: Q {
21+
func foo<T: P>(_: T, _: C3) where T.T: C3 {}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4
2+
3+
protocol P {
4+
associatedtype T
5+
}
6+
7+
protocol Q {
8+
func foo<T: P>(_: T, _: T.T) where T.T == Self
9+
}
10+
11+
class C1: Q {
12+
func foo<T: P>(_: T, _: C1) where T.T == C1 {} // expected-error{{instance method 'foo' in non-final class 'C1' cannot be used to satisfy requirement instance method 'foo' (in protocol 'Q') due to same-type requirement involving 'Self'}}}}
13+
// expected-note@-1{{consider weakening the same-type requirement 'T.T' == 'C1' to a superclass requirement}}{{41-43=:}}
14+
}
15+
16+
final class C2: Q {
17+
func foo<T: P>(_: T, _: C2) where T.T == C2 {}
18+
}
19+
20+
class C3: Q {
21+
func foo<T: P>(_: T, _: C3) where T.T: C3 {}
22+
}

0 commit comments

Comments
 (0)