Skip to content

Commit 64f12ff

Browse files
committed
Sema: Allow protocols with 'Self' constraints again
These two declarations are now equivalent: protocol P : SomeClass { ... } protocol P where Self : SomeClass { ... } There's a long, complicated story here: - Swift 4.2 rejected classes in the inheritance clause of a protocol, but it accepted the 'where' clause form, even though it didn't always work and would sometimes crash - Recently we got the inheritance clause form working, and added a diagnostic to ban the 'where' clause form, because we thought it would simplify name lookup to not have to consider the 'where' clause - However, we already had to support looking at the 'where' clause from name lookup anyway, because you could write extension P where Self : SomeClass { ... } - It turns out that despite the crashes, protocols with 'Self' constraints were already common enough that it was worth supporting the existing behavior, instead of banning it Fixes <rdar://problem/43028442>.
1 parent 36c8d94 commit 64f12ff

7 files changed

+801
-6
lines changed

lib/AST/NameLookup.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2542,6 +2542,8 @@ DirectlyReferencedTypeDecls UnderlyingTypeDeclsReferencedRequest::evaluate(
25422542
llvm::Expected<ClassDecl *>
25432543
SuperclassDeclRequest::evaluate(Evaluator &evaluator,
25442544
NominalTypeDecl *subject) const {
2545+
auto &Ctx = subject->getASTContext();
2546+
25452547
for (unsigned i : indices(subject->getInherited())) {
25462548
// Find the inherited declarations referenced at this position.
25472549
auto inheritedTypes = evaluateOrDefault(evaluator,
@@ -2551,7 +2553,7 @@ SuperclassDeclRequest::evaluate(Evaluator &evaluator,
25512553
SmallVector<ModuleDecl *, 2> modulesFound;
25522554
bool anyObject = false;
25532555
auto inheritedNominalTypes
2554-
= resolveTypeDeclsToNominal(evaluator, subject->getASTContext(),
2556+
= resolveTypeDeclsToNominal(evaluator, Ctx,
25552557
inheritedTypes, modulesFound, anyObject);
25562558

25572559
// Look for a class declaration.
@@ -2561,6 +2563,16 @@ SuperclassDeclRequest::evaluate(Evaluator &evaluator,
25612563
}
25622564
}
25632565

2566+
// Protocols also support '... where Self : Superclass'.
2567+
auto *proto = dyn_cast<ProtocolDecl>(subject);
2568+
if (proto == nullptr)
2569+
return nullptr;
2570+
2571+
auto selfBounds = getSelfBoundsFromWhereClause(proto);
2572+
for (auto inheritedNominal : selfBounds.decls)
2573+
if (auto classDecl = dyn_cast<ClassDecl>(inheritedNominal))
2574+
return classDecl;
2575+
25642576
return nullptr;
25652577
}
25662578

@@ -2641,5 +2653,18 @@ swift::getDirectlyInheritedNominalTypeDecls(
26412653
getDirectlyInheritedNominalTypeDecls(decl, i, result, anyObject);
26422654
}
26432655

2656+
auto *protoDecl = dyn_cast_or_null<ProtocolDecl>(typeDecl);
2657+
if (protoDecl == nullptr)
2658+
return result;
2659+
2660+
// FIXME: Refactor SelfBoundsFromWhereClauseRequest to dig out
2661+
// the source location.
2662+
SourceLoc loc = SourceLoc();
2663+
auto selfBounds = getSelfBoundsFromWhereClause(decl);
2664+
anyObject |= selfBounds.anyObject;
2665+
2666+
for (auto inheritedNominal : selfBounds.decls)
2667+
result.emplace_back(loc, inheritedNominal);
2668+
26442669
return result;
26452670
}

lib/Sema/TypeCheckDecl.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3044,7 +3044,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
30443044
checkAccessControl(TC, PD);
30453045

30463046
checkInheritanceClause(PD);
3047-
checkProtocolSelfRequirements(PD, PD);
30483047

30493048
TC.checkDeclCircularity(PD);
30503049
if (PD->isResilient())

test/IRGen/protocol_with_superclass.sil

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import Builtin
66
import Swift
77

88
// CHECK-LABEL: @"$s24protocol_with_superclass17ProtoRefinesClassMp" =
9-
// FIXME: Need to fill in superclass field in protocol descriptor
109

1110
class Concrete {}
1211

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// RUN: %target-swift-frontend -primary-file %s -emit-ir -module-name protocol_with_superclass | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime -DINT=i%target-ptrsize
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
import Swift
7+
8+
// CHECK-LABEL: @"$s24protocol_with_superclass17ProtoRefinesClassMp" =
9+
10+
class Concrete {}
11+
12+
class SubConcrete : Concrete {}
13+
14+
protocol SuperProto where Self : AnyObject {}
15+
16+
protocol ProtoRefinesClass where Self : SuperProto, Self : Concrete {}
17+
18+
protocol SubProto where Self : ProtoRefinesClass {}
19+
20+
protocol OtherProto {}
21+
22+
class Derived : Concrete, ProtoRefinesClass {}
23+
24+
class SubDerived : Derived {}
25+
26+
class MoreDerived : Concrete, SubProto {}
27+
28+
// CHECK-objc-LABEL: define hidden swiftcc void @checkExistentialDowncast(%T24protocol_with_superclass8ConcreteC*, {{.*}} {
29+
// CHECK-native-LABEL: define hidden swiftcc void @checkExistentialDowncast(%T24protocol_with_superclass8ConcreteC*, %swift.refcounted*, i8**, %T24protocol_with_superclass8ConcreteC*, i8**, %T24protocol_with_superclass8ConcreteC*, i8**, %T24protocol_with_superclass8ConcreteC*, i8**) {{.*}} {
30+
sil hidden @checkExistentialDowncast : $@convention(thin) (@owned Concrete, @owned SuperProto, @owned SuperProto & Concrete, @owned ProtoRefinesClass, @owned SubProto) -> () {
31+
bb0(%0 : @owned $Concrete, %1 : @owned $SuperProto, %2 : @owned $SuperProto & Concrete, %3 : @owned $ProtoRefinesClass, %4 : @owned $SubProto):
32+
33+
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %0 to %swift.type**
34+
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
35+
// CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %0 to i8*
36+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp"
37+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
38+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
39+
%5 = unconditional_checked_cast %0 : $Concrete to $ProtoRefinesClass
40+
41+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
42+
destroy_value %5 : $ProtoRefinesClass
43+
44+
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %0 to %swift.type**
45+
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
46+
// CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %0 to i8*
47+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp"
48+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
49+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
50+
%6 = unconditional_checked_cast %0 : $Concrete to $SubProto
51+
52+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
53+
destroy_value %6 : $SubProto
54+
55+
// CHECK-objc: [[ISA:%.*]] = call %swift.type* @swift_getObjectType(%objc_object* %{{[0-9]+}})
56+
// CHECK-objc-NEXT: [[OBJECT:%.*]] = bitcast %objc_object* %{{[0-9]+}} to i8*
57+
// CHECK-native: [[ISA_ADDR:%.*]] = getelementptr inbounds %swift.refcounted, %swift.refcounted* %1, i32 0, i32 0
58+
// CHECK-native-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
59+
// CHECK-native-NEXT: [[OBJECT:%.*]] = bitcast %swift.refcounted* %1 to i8*
60+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp"
61+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
62+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
63+
%7 = unconditional_checked_cast %1 : $SuperProto to $ProtoRefinesClass
64+
65+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
66+
destroy_value %7 : $ProtoRefinesClass
67+
68+
// CHECK-objc: [[ISA:%.*]] = call %swift.type* @swift_getObjectType(%objc_object* %{{[0-9]+}})
69+
// CHECK-objc-NEXT: [[OBJECT:%.*]] = bitcast %objc_object* %{{[0-9]+}} to i8*
70+
// CHECK-native: [[ISA_ADDR:%.*]] = getelementptr inbounds %swift.refcounted, %swift.refcounted* %1, i32 0, i32 0
71+
// CHECK-native-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
72+
// CHECK-native-NEXT: [[OBJECT:%.*]] = bitcast %swift.refcounted* %1 to i8*
73+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp"
74+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
75+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
76+
%8 = unconditional_checked_cast %1 : $SuperProto to $SubProto
77+
78+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
79+
destroy_value %8 : $SubProto
80+
81+
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to %swift.type**
82+
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
83+
// CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
84+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp"
85+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
86+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
87+
%9 = unconditional_checked_cast %2 : $SuperProto & Concrete to $ProtoRefinesClass
88+
89+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
90+
destroy_value %9 : $ProtoRefinesClass
91+
92+
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to %swift.type**
93+
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
94+
// CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
95+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp"
96+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
97+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
98+
%10 = unconditional_checked_cast %2 : $SuperProto & Concrete to $SubProto
99+
100+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
101+
destroy_value %10 : $SubProto
102+
103+
// CHECK: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
104+
// CHECK-NEXT: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s24protocol_with_superclass7DerivedCMa"(i{{[0-9]+}} 0)
105+
// CHECK-NEXT: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
106+
// CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8*
107+
// CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]])
108+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass7DerivedC*
109+
%11 = unconditional_checked_cast %3 : $ProtoRefinesClass to $Derived
110+
111+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass7DerivedC*)*)(%T24protocol_with_superclass7DerivedC* [[REFERENCE]])
112+
destroy_value %11 : $Derived
113+
114+
// CHECK: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
115+
// CHECK-NEXT: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s24protocol_with_superclass10SubDerivedCMa"(i{{[0-9]+}} 0)
116+
// CHECK-NEXT: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
117+
// CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8*
118+
// CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]])
119+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass10SubDerivedC*
120+
%12 = unconditional_checked_cast %3 : $ProtoRefinesClass to $SubDerived
121+
122+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass10SubDerivedC*)*)(%T24protocol_with_superclass10SubDerivedC* [[REFERENCE]])
123+
destroy_value %12 : $SubDerived
124+
125+
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to %swift.type**
126+
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
127+
// CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
128+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp"
129+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
130+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
131+
%13 = unconditional_checked_cast %3 : $ProtoRefinesClass to $OtherProto & Concrete
132+
133+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
134+
destroy_value %13 : $OtherProto & Concrete
135+
136+
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to %swift.type**
137+
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
138+
// CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
139+
// CHECK-NEXT: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s24protocol_with_superclass11SubConcreteCMa"(i{{[0-9]+}} 0)
140+
// CHECK-NEXT: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
141+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], %swift.type* [[METADATA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp"
142+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
143+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass11SubConcreteC*
144+
%14 = unconditional_checked_cast %3 : $ProtoRefinesClass to $OtherProto & SubConcrete
145+
146+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass11SubConcreteC*)*)(%T24protocol_with_superclass11SubConcreteC* [[REFERENCE]])
147+
destroy_value %14 : $OtherProto & SubConcrete
148+
149+
// CHECK: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
150+
// CHECK-NEXT: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s24protocol_with_superclass11MoreDerivedCMa"(i{{[0-9]+}} 0)
151+
// CHECK-NEXT: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
152+
// CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8*
153+
// CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]])
154+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass11MoreDerivedC*
155+
%15 = unconditional_checked_cast %4 : $SubProto to $MoreDerived
156+
157+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass11MoreDerivedC*)*)(%T24protocol_with_superclass11MoreDerivedC* [[REFERENCE]])
158+
destroy_value %15 : $MoreDerived
159+
160+
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to %swift.type**
161+
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
162+
// CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T24protocol_with_superclass8ConcreteC* %{{[0-9]+}} to i8*
163+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp"
164+
// CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
165+
// CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC*
166+
%16 = unconditional_checked_cast %4 : $SubProto to $OtherProto & Concrete
167+
168+
// CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]])
169+
destroy_value %16 : $OtherProto & Concrete
170+
171+
%result = tuple ()
172+
return %result : $()
173+
}
174+
175+
sil_vtable Concrete {}
176+
sil_vtable SubConcrete {}
177+
sil_vtable Derived {}
178+
sil_vtable SubDerived {}
179+
sil_vtable MoreDerived {}

0 commit comments

Comments
 (0)