Skip to content

Commit 926e371

Browse files
committed
Only permit inheritance of protocol conformance when it is semantically valid.
A protocol conformance of a class A to a protocol P can be inherited by a subclass B of A unless - A requirement of P refers to Self (not an associated type thereof) in its signature, + *except* when Self is the result type of the method in P and the corresponding witness for A's conformance to B is a DynamicSelf method. Remove the uses of DynamicSelf from the literal protocols, going back to Self. The fact that the conformances of NSDictionary, NSArray, NSString, etc. to the corresponding literal protocols use witnesses that return DynamicSelf makes NSMutableDictionary, NSMutableArray, NSMutableString, and other subclasses still conform to the protocol. We also correctly reject attempts to (for example) create an NSDecimalNumber from a numeric literal, because NSNumber doesn't provide a suitable factory method by which any subclass can be literal convertible. Swift SVN r14204
1 parent e77e43f commit 926e371

File tree

4 files changed

+282
-7
lines changed

4 files changed

+282
-7
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
namespace swift {
3030

3131
class ASTContext;
32+
class DiagnosticEngine;
3233
class GenericParamList;
3334
class NormalProtocolConformance;
3435
class ProtocolConformance;
@@ -199,7 +200,10 @@ class ProtocolConformance {
199200

200201
/// Get the underlying normal conformance.
201202
const NormalProtocolConformance *getRootNormalConformance() const;
202-
203+
204+
/// Determine whether this conformance is inheritable by subclasses.
205+
bool isInheritable(LazyResolver *resolver) const;
206+
203207
/// Determine whether the witness for the given requirement
204208
/// is either the default definition or was otherwise deduced.
205209
///
@@ -253,16 +257,28 @@ class ProtocolConformance {
253257
/// providing the witnesses \c A.foo and \c B<T>.foo, respectively, for the
254258
/// requirement \c foo.
255259
class NormalProtocolConformance : public ProtocolConformance {
260+
/// Describes whether this conformance is inheritable to subclasses or not.
261+
enum class IsInheritableKind {
262+
/// We haven't checked yet whether this conformance is inheritable.
263+
Unknown,
264+
/// This conformance is inheritable.
265+
Inheritable,
266+
/// Ths conformance is not inheritable.
267+
NotInheritable
268+
};
269+
256270
/// \brief The protocol being conformed to and its current state.
257271
llvm::PointerIntPair<ProtocolDecl *, 2, ProtocolConformanceState>
258272
ProtocolAndState;
259273

260274
/// The location of this protocol conformance in the source.
261275
SourceLoc Loc;
262276

263-
/// \brief The declaration context containing the ExtensionDecl or
264-
/// NominalTypeDecl that declared the conformance.
265-
DeclContext *DC;
277+
/// The declaration context containing the ExtensionDecl or
278+
/// NominalTypeDecl that declared the conformance, along with whether this
279+
/// conformance is inheritable.
280+
mutable llvm::PointerIntPair<DeclContext *, 2, IsInheritableKind>
281+
DCAndInheritable;
266282

267283
/// \brief The mapping of individual requirements in the protocol over to
268284
/// the declarations that satisfy those requirements.
@@ -286,10 +302,13 @@ class NormalProtocolConformance : public ProtocolConformance {
286302
SourceLoc loc, DeclContext *dc,
287303
ProtocolConformanceState state)
288304
: ProtocolConformance(ProtocolConformanceKind::Normal, conformingType),
289-
ProtocolAndState(protocol, state), Loc(loc), DC(dc)
305+
ProtocolAndState(protocol, state), Loc(loc),
306+
DCAndInheritable(dc, IsInheritableKind::Unknown)
290307
{
291308
}
292309

310+
bool isInheritableSlow(LazyResolver *resolver) const;
311+
293312
public:
294313
/// Get the protocol being conformed to.
295314
ProtocolDecl *getProtocol() const { return ProtocolAndState.getPointer(); }
@@ -300,13 +319,13 @@ class NormalProtocolConformance : public ProtocolConformance {
300319
/// Get the declaration context that contains the conforming extension or
301320
/// nominal type declaration.
302321
DeclContext *getDeclContext() const {
303-
return DC;
322+
return DCAndInheritable.getPointer();
304323
}
305324

306325
/// Set the declaration context that contains the conforming extension or
307326
/// nominal type declaration.
308327
void setDeclContext(DeclContext *dc) {
309-
DC = dc;
328+
DCAndInheritable.setPointer(dc);
310329
}
311330

312331
/// Retrieve the state of this conformance.
@@ -319,6 +338,20 @@ class NormalProtocolConformance : public ProtocolConformance {
319338
ProtocolAndState.setInt(state);
320339
}
321340

341+
/// Determine whether this conformance is inheritable by subclasses.
342+
bool isInheritable(LazyResolver *resolver) const {
343+
switch (DCAndInheritable.getInt()) {
344+
case IsInheritableKind::Inheritable:
345+
return true;
346+
347+
case IsInheritableKind::NotInheritable:
348+
return false;
349+
350+
case IsInheritableKind::Unknown:
351+
return isInheritableSlow(resolver);
352+
}
353+
}
354+
322355
/// Retrieve the type witness corresponding to the given associated type
323356
/// requirement.
324357
const Substitution &getTypeWitness(AssociatedTypeDecl *assocType,
@@ -384,6 +417,7 @@ class NormalProtocolConformance : public ProtocolConformance {
384417
DefaultedDefinitions.insert(requirement);
385418
}
386419

420+
387421
static bool classof(const ProtocolConformance *conformance) {
388422
return conformance->getKind() == ProtocolConformanceKind::Normal;
389423
}
@@ -459,6 +493,11 @@ class SpecializedProtocolConformance : public ProtocolConformance,
459493
return GenericConformance->getState();
460494
}
461495

496+
/// Determine whether this conformance is inheritable by subclasses.
497+
bool isInheritable(LazyResolver *resolver) const {
498+
return GenericConformance->isInheritable(resolver);
499+
}
500+
462501
/// Retrieve the type witness for the given associated type.
463502
const Substitution &getTypeWitness(AssociatedTypeDecl *assocType,
464503
LazyResolver *resolver) const;
@@ -547,6 +586,12 @@ class InheritedProtocolConformance : public ProtocolConformance,
547586
return InheritedConformance->getState();
548587
}
549588

589+
/// Determine whether this conformance is inheritable by subclasses.
590+
bool isInheritable(LazyResolver *resolver) const {
591+
// Always inheritable, because it was itself inherited.
592+
return true;
593+
}
594+
550595
/// Retrieve the type witness for the given associated type.
551596
const Substitution &getTypeWitness(AssociatedTypeDecl *assocType,
552597
LazyResolver *resolver) const {

lib/AST/Module.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,12 @@ LookupConformanceResult Module::lookupConformance(Type type,
697697
return inheritedConformance;
698698

699699
case ConformanceKind::Conforms:
700+
// If the inherited conformance is not in fact inheritable, then
701+
// we're done.
702+
if (!inheritedConformance.getPointer()->isInheritable(resolver)) {
703+
return { nullptr, ConformanceKind::DoesNotConform };
704+
}
705+
700706
// Create inherited conformance below.
701707
break;
702708
}

lib/AST/ProtocolConformance.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/Module.h"
2121
#include "swift/AST/Substitution.h"
2222
#include "swift/AST/Types.h"
23+
#include "swift/AST/TypeWalker.h"
2324

2425
using namespace swift;
2526

@@ -270,6 +271,10 @@ ProtocolConformance::getRootNormalConformance() const {
270271
return cast<NormalProtocolConformance>(C);
271272
}
272273

274+
bool ProtocolConformance::isInheritable(LazyResolver *resolver) const {
275+
CONFORMANCE_SUBCLASS_DISPATCH(isInheritable, (resolver))
276+
}
277+
273278
ProtocolConformance *ProtocolConformance::subst(Module *module,
274279
Type substType,
275280
ArrayRef<Substitution> subs,
@@ -369,3 +374,127 @@ ProtocolConformance::getInheritedConformance(ProtocolDecl *protocol) const {
369374
&& "inherited conformance does not match type");
370375
return foundInherited;
371376
}
377+
378+
namespace {
379+
/// Describes whether a requirement refers to 'Self', for use in the
380+
/// is-inheritable check.
381+
enum class SelfReferenceKind {
382+
/// The type does not refer to 'Self' at all.
383+
No,
384+
/// The type refers to 'Self', but only as the result type of a method.
385+
Result,
386+
/// The type refers to 'Self' in some position that is not the result type
387+
/// of a method.
388+
Yes
389+
};
390+
}
391+
392+
/// Determine whether the given type is the 'Self' generic parameter
393+
/// of a protocol.
394+
static bool isSelf(Type type) {
395+
if (auto genericParam = type->getAs<GenericTypeParamType>()) {
396+
return genericParam->getDepth() == 0 && genericParam->getIndex() == 0;
397+
}
398+
399+
return false;
400+
}
401+
402+
/// Determine whether the given type contains a reference to the
403+
/// 'Self' generic parameter of a protocol that is not the base of a
404+
/// dependent member expression.
405+
static bool containsSelf(Type type) {
406+
struct SelfWalker : public TypeWalker {
407+
bool FoundSelf = false;
408+
409+
virtual Action walkToTypePre(Type ty) {
410+
// If we found a reference to 'Self', note it and stop.
411+
if (isSelf(ty)) {
412+
FoundSelf = true;
413+
return Action::Stop;
414+
}
415+
416+
// Don't recurse into the base of a dependent member type: it
417+
// doesn't contain a bare 'Self'.
418+
if (ty->is<DependentMemberType>())
419+
return Action::SkipChildren;
420+
421+
return Action::Continue;
422+
}
423+
} selfWalker;
424+
425+
type.walk(selfWalker);
426+
return selfWalker.FoundSelf;
427+
}
428+
429+
/// Find the bare Self references within the given requirement.
430+
static SelfReferenceKind findSelfReferences(ValueDecl *value) {
431+
// Types never refer to 'Self'.
432+
if (isa<TypeDecl>(value))
433+
return SelfReferenceKind::No;
434+
435+
// If the function requirement returns Self and has no other
436+
// reference to Self, note that.
437+
if (auto func = dyn_cast<FuncDecl>(value)) {
438+
auto type = func->getInterfaceType();
439+
// Skip the 'self' type.
440+
type = type->castTo<AnyFunctionType>()->getResult();
441+
for (unsigned i = 1, n = func->getNumParamPatterns(); i != n; ++i) {
442+
// Check whether the input type contains Self anywhere.
443+
auto fnType = type->castTo<AnyFunctionType>();
444+
if (containsSelf(fnType->getInput()))
445+
return SelfReferenceKind::Yes;
446+
447+
type = fnType->getResult();
448+
}
449+
450+
// The result type remains.
451+
return (isSelf(type) || func->hasDynamicSelf())
452+
? SelfReferenceKind::Result
453+
: containsSelf(type) ? SelfReferenceKind::Yes
454+
: SelfReferenceKind::No;
455+
}
456+
457+
return containsSelf(value->getInterfaceType()) ? SelfReferenceKind::Yes
458+
: SelfReferenceKind::No;
459+
}
460+
461+
bool NormalProtocolConformance::isInheritableSlow(LazyResolver *resolver) const{
462+
auto classDecl = getType()->getClassOrBoundGenericClass();
463+
assert(classDecl && "Conformance can't be inheritable, ever");
464+
465+
for (auto member : getProtocol()->getMembers()) {
466+
auto req = dyn_cast<ValueDecl>(member);
467+
if (!req)
468+
continue;
469+
470+
// Skip accessors.
471+
if (isa<FuncDecl>(req) && cast<FuncDecl>(req)->isAccessor())
472+
continue;
473+
474+
// Check the kinds of references to Self that show up in the given
475+
// requirement.
476+
switch (findSelfReferences(req)) {
477+
case SelfReferenceKind::No:
478+
continue;
479+
480+
case SelfReferenceKind::Result: {
481+
// When only the result type is 'Self', we can inherit the
482+
// conformance if the witness returns DynamicSelf.
483+
auto func = cast_or_null<FuncDecl>(
484+
getWitness(req, resolver).getDecl());
485+
if (func && func->hasDynamicSelf())
486+
continue;
487+
// Fall through
488+
}
489+
490+
case SelfReferenceKind::Yes: {
491+
DCAndInheritable.setInt(IsInheritableKind::NotInheritable);
492+
return false;
493+
}
494+
}
495+
}
496+
497+
//
498+
DCAndInheritable.setInt(IsInheritableKind::Inheritable);
499+
return true;
500+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// RUN: %swift -parse %s -verify
2+
3+
// Never inheritable: method with 'Self' in its signature
4+
protocol P1 {
5+
func f1(x: Self) -> Bool
6+
}
7+
8+
// Never inheritable: property with 'Self' in its signature.
9+
protocol P2 {
10+
var prop2: Self { get }
11+
}
12+
13+
// Never inheritable: subscript with 'Self' in its signature
14+
protocol P3 {
15+
subscript (i: Int) -> Self { get }
16+
}
17+
18+
// Never inheritable: subscript with 'Self' in its signature
19+
protocol P4 {
20+
subscript (s: Self) -> Int { get }
21+
}
22+
23+
// Potentially inheritable: method returning Self
24+
protocol P5 {
25+
func f5() -> Self
26+
}
27+
28+
// Inheritable: method returning DynamicSelf
29+
protocol P6 {
30+
func f6() -> DynamicSelf
31+
}
32+
33+
// Inheritable: method involving associated type.
34+
protocol P7 {
35+
typealias Assoc
36+
func f7() -> Assoc
37+
}
38+
39+
// Class A conforms to everything.
40+
class A : P1, P2, P3, P4, P5, P6, P7 {
41+
// P1
42+
func f1(a: A) -> Bool { return true }
43+
44+
// P2
45+
var prop2: A {
46+
return self
47+
}
48+
49+
// P3
50+
subscript (i: Int) -> A {
51+
get:
52+
return self
53+
}
54+
55+
// P4
56+
subscript (a: A) -> Int {
57+
get:
58+
return 5
59+
}
60+
61+
// P5
62+
func f5() -> A { return self }
63+
64+
// P6
65+
func f6() -> DynamicSelf { return self }
66+
67+
// P7
68+
func f7() -> Int { return 5 }
69+
}
70+
71+
// Class B inherits A; gets all of its inheritable conformances.
72+
class B : A { }
73+
74+
func testB(b: B) {
75+
var p1: P1 = b // expected-error{{type 'B' does not conform to protocol 'P1'}}
76+
var p2: P2 = b // expected-error{{type 'B' does not conform to protocol 'P2'}}
77+
var p3: P3 = b // expected-error{{type 'B' does not conform to protocol 'P3'}}
78+
var p4: P4 = b // expected-error{{type 'B' does not conform to protocol 'P4'}}
79+
var p5: P5 = b // expected-error{{type 'B' does not conform to protocol 'P5'}}
80+
var p6: P6 = b
81+
var p7: P7 = b
82+
}
83+
84+
// Class A5 conforms to P5 in an inheritable manner.
85+
class A5 : P5 {
86+
// P5
87+
func f5() -> DynamicSelf { return self }
88+
}
89+
90+
// Class B5 inherits A5; gets all of its inheritable conformances.
91+
class B5 : A5 { }
92+
93+
func testB5(b5: B5) {
94+
var p5: P5 = b5 // okay
95+
}

0 commit comments

Comments
 (0)