Skip to content

Commit e34c409

Browse files
authored
Merge pull request #17816 from slavapestov/protocol-superclass-part-3
Protocols with superclass constraints - part 3
2 parents ebd01d1 + 87b2ddb commit e34c409

File tree

6 files changed

+558
-44
lines changed

6 files changed

+558
-44
lines changed

lib/AST/NameLookup.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,12 +1711,24 @@ bool DeclContext::lookupQualified(Type type,
17111711
SmallVector<NominalTypeDecl *, 4> stack;
17121712
llvm::SmallPtrSet<NominalTypeDecl *, 4> visited;
17131713

1714+
// Note that we don't have to visit the superclass of a protocol.
1715+
// If we started with an archetype or existential, we'll visit the
1716+
// superclass because we will have added it to the stack upfront.
1717+
//
1718+
// If we started with a concrete class conforming to a protocol
1719+
// with a superclass, we will visit the superclass from the
1720+
// concrete type.
1721+
bool checkProtocolSuperclass = false;
1722+
17141723
// Handle nominal types.
17151724
bool wantProtocolMembers = (options & NL_ProtocolMembers);
17161725
bool wantLookupInAllClasses = false;
17171726
if (auto nominal = type->getAnyNominal()) {
17181727
visited.insert(nominal);
17191728
stack.push_back(nominal);
1729+
1730+
if (isa<ProtocolDecl>(nominal))
1731+
checkProtocolSuperclass = true;
17201732
}
17211733
// Handle archetypes
17221734
else if (auto archetypeTy = type->getAs<ArchetypeType>()) {
@@ -1746,9 +1758,9 @@ bool DeclContext::lookupQualified(Type type,
17461758
}
17471759

17481760
if (auto superclass = layout.explicitSuperclass) {
1749-
auto *nominalDecl = superclass->getClassOrBoundGenericClass();
1750-
if (visited.insert(nominalDecl).second)
1751-
stack.push_back(nominalDecl);
1761+
auto *superclassDecl = superclass->getClassOrBoundGenericClass();
1762+
if (visited.insert(superclassDecl).second)
1763+
stack.push_back(superclassDecl);
17521764
}
17531765
} else {
17541766
llvm_unreachable("Bad type for qualified lookup");
@@ -1844,18 +1856,21 @@ bool DeclContext::lookupQualified(Type type,
18441856
}
18451857
}
18461858

1847-
if (auto protocolDecl = dyn_cast<ProtocolDecl>(current)) {
1848-
if (auto superclassDecl = protocolDecl->getSuperclassDecl())
1849-
if (visited.insert(superclassDecl).second)
1850-
stack.push_back(superclassDecl);
1851-
}
1852-
18531859
// If we're not looking at a protocol and we're not supposed to
18541860
// visit the protocols that this type conforms to, skip the next
18551861
// step.
18561862
if (!wantProtocolMembers && !currentIsProtocol)
18571863
continue;
18581864

1865+
if (checkProtocolSuperclass) {
1866+
if (auto *protoDecl = dyn_cast<ProtocolDecl>(current)) {
1867+
if (auto superclassDecl = protoDecl->getSuperclassDecl()) {
1868+
visited.insert(superclassDecl);
1869+
stack.push_back(superclassDecl);
1870+
}
1871+
}
1872+
}
1873+
18591874
SmallVector<ProtocolDecl *, 4> protocols;
18601875
for (auto proto : current->getAllProtocols()) {
18611876
if (visited.insert(proto).second) {

lib/AST/Type.cpp

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,15 @@ Type ExistentialLayout::getSuperclass() const {
300300
return explicitSuperclass;
301301

302302
for (auto proto : getProtocols()) {
303-
if (auto superclass = proto->getSuperclass())
303+
// If we have a generic signature, check there, because it
304+
// will pick up superclass constraints from protocols that we
305+
// refine as well.
306+
auto *protoDecl = proto->getDecl();
307+
if (auto genericSig = protoDecl->getGenericSignature()) {
308+
if (auto superclass = genericSig->getSuperclassBound(
309+
protoDecl->getSelfInterfaceType()))
310+
return superclass;
311+
} else if (auto superclass = protoDecl->getSuperclass())
304312
return superclass;
305313
}
306314

@@ -1465,11 +1473,8 @@ Type TypeBase::getSuperclass() {
14651473
if (auto dynamicSelfTy = getAs<DynamicSelfType>())
14661474
return dynamicSelfTy->getSelfType();
14671475

1468-
if (auto protocolTy = getAs<ProtocolType>())
1469-
return protocolTy->getDecl()->getSuperclass();
1470-
1471-
if (auto compositionTy = getAs<ProtocolCompositionType>())
1472-
return compositionTy->getExistentialLayout().getSuperclass();
1476+
if (isExistentialType())
1477+
return getExistentialLayout().getSuperclass();
14731478

14741479
// No other types have superclasses.
14751480
return Type();
@@ -3205,14 +3210,12 @@ const DependentMemberType *TypeBase::findUnresolvedDependentMemberType() {
32053210
}
32063211

32073212
static Type getConcreteTypeForSuperclassTraversing(Type t) {
3208-
if (!t->getAnyNominal()) {
3209-
if (auto archetype = t->getAs<ArchetypeType>()) {
3210-
return archetype->getSuperclass();
3211-
} else if (auto dynamicSelfTy = t->getAs<DynamicSelfType>()) {
3212-
return dynamicSelfTy->getSelfType();
3213-
} else if (auto compositionTy = t->getAs<ProtocolCompositionType>()) {
3214-
return compositionTy->getExistentialLayout().explicitSuperclass;
3215-
}
3213+
if (t->isExistentialType()) {
3214+
return t->getExistentialLayout().getSuperclass();
3215+
} if (auto archetype = t->getAs<ArchetypeType>()) {
3216+
return archetype->getSuperclass();
3217+
} else if (auto dynamicSelfTy = t->getAs<DynamicSelfType>()) {
3218+
return dynamicSelfTy->getSelfType();
32163219
}
32173220
return t;
32183221
}

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ static bool isNonFinalClass(Type type) {
320320
if (auto super = archetype->getSuperclass())
321321
return isNonFinalClass(super);
322322

323+
if (type->isExistentialType())
324+
return true;
325+
323326
return false;
324327
}
325328

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// RUN: %target-swift-emit-silgen -enable-sil-ownership %s | tee /tmp/xxx | %FileCheck %s
2+
3+
// Protocols with superclass-constrained Self.
4+
5+
class Concrete {
6+
typealias ConcreteAlias = String
7+
8+
func concreteMethod(_: ConcreteAlias) {}
9+
}
10+
11+
class Generic<T> : Concrete {
12+
typealias GenericAlias = (T, T)
13+
14+
func genericMethod(_: GenericAlias) {}
15+
}
16+
17+
protocol BaseProto {}
18+
19+
protocol ProtoRefinesClass : Generic<Int>, BaseProto {
20+
func requirementUsesClassTypes(_: ConcreteAlias, _: GenericAlias)
21+
}
22+
23+
extension ProtoRefinesClass {
24+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass17ProtoRefinesClassPAAE019extensionMethodUsesF5TypesyySS_Si_SittF : $@convention(method) <Self where Self : ProtoRefinesClass> (@guaranteed String, Int, Int, @guaranteed Self) -> ()
25+
func extensionMethodUsesClassTypes(_ x: ConcreteAlias, _ y: GenericAlias) {
26+
_ = ConcreteAlias.self
27+
_ = GenericAlias.self
28+
29+
// CHECK: [[SELF:%.*]] = copy_value %3 : $Self
30+
// CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic<Int>
31+
// CHECK-NEXT: [[UPCAST2:%.*]] = upcast [[UPCAST]] : $Generic<Int> to $Concrete
32+
// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[UPCAST2]] : $Concrete
33+
// CHECK-NEXT: [[METHOD:%.*]] = class_method [[BORROW:%.*]] : $Concrete, #Concrete.concreteMethod!1 : (Concrete) -> (String) -> (), $@convention(method) (@guaranteed String, @guaranteed Concrete) -> ()
34+
// CHECK-NEXT: apply [[METHOD]](%0, [[BORROW]])
35+
// CHECK-NEXT: end_borrow [[BORROW]]
36+
// CHECK-NEXT: destroy_value [[UPCAST2]]
37+
concreteMethod(x)
38+
39+
// CHECK: [[SELF:%.*]] = copy_value %3 : $Self
40+
// CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic<Int>
41+
// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[UPCAST]] : $Generic<Int>
42+
// CHECK: [[METHOD:%.*]] = class_method [[BORROW:%.*]] : $Generic<Int>, #Generic.genericMethod!1 : <T> (Generic<T>) -> ((T, T)) -> (), $@convention(method) <τ_0_0> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @guaranteed Generic<τ_0_0>) -> ()
43+
// CHECK-NEXT: apply [[METHOD]]<Int>({{.*}}, [[BORROW]])
44+
// CHECK: end_borrow [[BORROW]]
45+
// CHECK-NEXT: destroy_value [[UPCAST]]
46+
genericMethod(y)
47+
48+
// CHECK: [[SELF:%.*]] = copy_value %3 : $Self
49+
// CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic<Int>
50+
// CHECK-NEXT: destroy_value [[UPCAST]] : $Generic<Int>
51+
let _: Generic<Int> = self
52+
53+
// CHECK: [[SELF:%.*]] = copy_value %3 : $Self
54+
// CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic<Int>
55+
// CHECK-NEXT: [[UPCAST2:%.*]] = upcast [[UPCAST]] : $Generic<Int> to $Concrete
56+
// CHECK-NEXT: destroy_value [[UPCAST2]] : $Concrete
57+
let _: Concrete = self
58+
59+
// CHECK: [[BOX:%.*]] = alloc_stack $BaseProto
60+
// CHECK-NEXT: [[ADDR:%.*]] = init_existential_addr [[BOX]] : $*BaseProto, $Self
61+
// CHECK-NEXT: [[SELF:%.*]] = copy_value %3 : $Self
62+
// CHECK-NEXT: store [[SELF]] to [init] [[ADDR]] : $*Self
63+
// CHECK-NEXT: destroy_addr [[BOX]] : $*BaseProto
64+
// CHECK-NEXT: dealloc_stack [[BOX]] : $*BaseProto
65+
let _: BaseProto = self
66+
67+
// CHECK: [[SELF:%.*]] = copy_value %3 : $Self
68+
// CHECK-NEXT: [[EXISTENTIAL:%.*]] = init_existential_ref [[SELF]] : $Self : $Self, $Generic<Int> & BaseProto
69+
let _: BaseProto & Generic<Int> = self
70+
71+
// CHECK: [[SELF:%.*]] = copy_value %3 : $Self
72+
// CHECK-NEXT: [[EXISTENTIAL:%.*]] = init_existential_ref [[SELF]] : $Self : $Self, $Concrete & BaseProto
73+
let _: BaseProto & Concrete = self
74+
75+
// CHECK: return
76+
}
77+
}
78+
79+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass22usesProtoRefinesClass1yyAA0eF5Class_pF : $@convention(thin) (@guaranteed ProtoRefinesClass) -> ()
80+
func usesProtoRefinesClass1(_ t: ProtoRefinesClass) {
81+
let x: ProtoRefinesClass.ConcreteAlias = "hi"
82+
_ = ProtoRefinesClass.ConcreteAlias.self
83+
84+
t.concreteMethod(x)
85+
86+
let y: ProtoRefinesClass.GenericAlias = (1, 2)
87+
_ = ProtoRefinesClass.GenericAlias.self
88+
89+
t.genericMethod(y)
90+
91+
t.requirementUsesClassTypes(x, y)
92+
93+
let _: Generic<Int> = t
94+
let _: Concrete = t
95+
let _: BaseProto = t
96+
let _: BaseProto & Generic<Int> = t
97+
let _: BaseProto & Concrete = t
98+
}
99+
100+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass22usesProtoRefinesClass2yyxAA0eF5ClassRzlF : $@convention(thin) <T where T : ProtoRefinesClass> (@guaranteed T) -> ()
101+
func usesProtoRefinesClass2<T : ProtoRefinesClass>(_ t: T) {
102+
let x: T.ConcreteAlias = "hi"
103+
_ = T.ConcreteAlias.self
104+
105+
t.concreteMethod(x)
106+
107+
let y: T.GenericAlias = (1, 2)
108+
_ = T.GenericAlias.self
109+
110+
t.genericMethod(y)
111+
112+
t.requirementUsesClassTypes(x, y)
113+
114+
let _: Generic<Int> = t
115+
let _: Concrete = t
116+
let _: BaseProto = t
117+
let _: BaseProto & Generic<Int> = t
118+
let _: BaseProto & Concrete = t
119+
}
120+
121+
class GoodConformingClass : Generic<Int>, ProtoRefinesClass {
122+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass19GoodConformingClassC015requirementUsesF5TypesyySS_Si_SittF : $@convention(method) (@guaranteed String, Int, Int, @guaranteed GoodConformingClass) -> ()
123+
func requirementUsesClassTypes(_ x: ConcreteAlias, _ y: GenericAlias) {
124+
_ = ConcreteAlias.self
125+
_ = GenericAlias.self
126+
127+
concreteMethod(x)
128+
129+
genericMethod(y)
130+
}
131+
}
132+
133+
protocol ProtoRefinesProtoWithClass : ProtoRefinesClass {}
134+
135+
extension ProtoRefinesProtoWithClass {
136+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass012ProtoRefinesD9WithClassPAAE026anotherExtensionMethodUsesG5TypesyySS_Si_SittF : $@convention(method) <Self where Self : ProtoRefinesProtoWithClass> (@guaranteed String, Int, Int, @guaranteed Self) -> ()
137+
func anotherExtensionMethodUsesClassTypes(_ x: ConcreteAlias, _ y: GenericAlias) {
138+
_ = ConcreteAlias.self
139+
_ = GenericAlias.self
140+
141+
concreteMethod(x)
142+
genericMethod(y)
143+
144+
let _: Generic<Int> = self
145+
let _: Concrete = self
146+
let _: BaseProto = self
147+
let _: BaseProto & Generic<Int> = self
148+
let _: BaseProto & Concrete = self
149+
}
150+
}
151+
152+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass016usesProtoRefinesE10WithClass1yyAA0efeG5Class_pF : $@convention(thin) (@guaranteed ProtoRefinesProtoWithClass) -> ()
153+
func usesProtoRefinesProtoWithClass1(_ t: ProtoRefinesProtoWithClass) {
154+
let x: ProtoRefinesProtoWithClass.ConcreteAlias = "hi"
155+
_ = ProtoRefinesProtoWithClass.ConcreteAlias.self
156+
157+
t.concreteMethod(x)
158+
159+
let y: ProtoRefinesProtoWithClass.GenericAlias = (1, 2)
160+
_ = ProtoRefinesProtoWithClass.GenericAlias.self
161+
162+
t.genericMethod(y)
163+
164+
t.requirementUsesClassTypes(x, y)
165+
166+
let _: Generic<Int> = t
167+
let _: Concrete = t
168+
let _: BaseProto = t
169+
let _: BaseProto & Generic<Int> = t
170+
let _: BaseProto & Concrete = t
171+
}
172+
173+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass016usesProtoRefinesE10WithClass2yyxAA0efeG5ClassRzlF : $@convention(thin) <T where T : ProtoRefinesProtoWithClass> (@guaranteed T) -> ()
174+
func usesProtoRefinesProtoWithClass2<T : ProtoRefinesProtoWithClass>(_ t: T) {
175+
let x: T.ConcreteAlias = "hi"
176+
_ = T.ConcreteAlias.self
177+
178+
t.concreteMethod(x)
179+
180+
let y: T.GenericAlias = (1, 2)
181+
_ = T.GenericAlias.self
182+
183+
t.genericMethod(y)
184+
185+
t.requirementUsesClassTypes(x, y)
186+
187+
let _: Generic<Int> = t
188+
let _: Concrete = t
189+
let _: BaseProto = t
190+
let _: BaseProto & Generic<Int> = t
191+
let _: BaseProto & Concrete = t
192+
}
193+
194+
class ClassWithInits<T> {
195+
init(notRequiredInit: ()) {}
196+
197+
required init(requiredInit: ()) {}
198+
}
199+
200+
protocol ProtocolWithClassInits : ClassWithInits<Int> {}
201+
202+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass26useProtocolWithClassInits1yyAA0efG5Inits_pXpF : $@convention(thin) (@thick ProtocolWithClassInits.Type) -> ()
203+
func useProtocolWithClassInits1(_ t: ProtocolWithClassInits.Type) {
204+
// CHECK: [[OPENED:%.*]] = open_existential_metatype %0 : $@thick ProtocolWithClassInits.Type
205+
// CHECK-NEXT: [[UPCAST:%.*]] = upcast [[OPENED]] : $@thick (@opened("{{.*}}") ProtocolWithClassInits).Type to $@thick ClassWithInits<Int>.Type
206+
// CHECK-NEXT: [[METHOD:%.*]] = class_method [[UPCAST]] : $@thick ClassWithInits<Int>.Type, #ClassWithInits.init!allocator.1 : <T> (ClassWithInits<T>.Type) -> (()) -> ClassWithInits<T>, $@convention(method) <τ_0_0> (@thick ClassWithInits<τ_0_0>.Type) -> @owned ClassWithInits<τ_0_0>
207+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]]<Int>([[UPCAST]])
208+
// CHECK-NEXT: [[CAST:%.*]] = unchecked_ref_cast [[RESULT]] : $ClassWithInits<Int> to $@opened("{{.*}}") ProtocolWithClassInits
209+
// CHECK-NEXT: [[EXISTENTIAL:%.*]] = init_existential_ref [[CAST]] : $@opened("{{.*}}") ProtocolWithClassInits : $@opened("{{.*}}") ProtocolWithClassInits, $ProtocolWithClassInits
210+
// CHECK-NEXT: destroy_value [[EXISTENTIAL]]
211+
let _: ProtocolWithClassInits = t.init(requiredInit: ())
212+
}
213+
214+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass26useProtocolWithClassInits2yyxmAA0efG5InitsRzlF : $@convention(thin) <T where T : ProtocolWithClassInits> (@thick T.Type) -> ()
215+
func useProtocolWithClassInits2<T : ProtocolWithClassInits>(_ t: T.Type) {
216+
let _: T = T(requiredInit: ())
217+
218+
let _: T = t.init(requiredInit: ())
219+
}
220+
221+
protocol ProtocolRefinesClassInits : ProtocolWithClassInits {}
222+
223+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass29useProtocolRefinesClassInits1yyAA0efG5Inits_pXpF : $@convention(thin) (@thick ProtocolRefinesClassInits.Type) -> ()
224+
func useProtocolRefinesClassInits1(_ t: ProtocolRefinesClassInits.Type) {
225+
let _: ProtocolRefinesClassInits = t.init(requiredInit: ())
226+
}
227+
228+
// CHECK-LABEL: sil hidden @$S24protocol_with_superclass29useProtocolRefinesClassInits2yyxmAA0efG5InitsRzlF : $@convention(thin) <T where T : ProtocolRefinesClassInits> (@thick T.Type) -> ()
229+
func useProtocolRefinesClassInits2<T : ProtocolRefinesClassInits>(_ t: T.Type) {
230+
let _: T = T(requiredInit: ())
231+
232+
let _: T = t.init(requiredInit: ())
233+
}
234+
235+
class ClassWithDefault<T> {
236+
func makeT() -> T { }
237+
}
238+
239+
protocol SillyDefault : ClassWithDefault<Int> {
240+
func makeT() -> Int
241+
}
242+
243+
class ConformsToSillyDefault : ClassWithDefault<Int>, SillyDefault {}
244+
245+
// CHECK-LABEL: sil private [transparent] [thunk] @$S24protocol_with_superclass22ConformsToSillyDefaultCAA0fG0A2aDP5makeTSiyFTW : $@convention(witness_method: SillyDefault) (@guaranteed ConformsToSillyDefault) -> Int
246+
// CHECK: class_method %1 : $ClassWithDefault<Int>, #ClassWithDefault.makeT!1 : <T> (ClassWithDefault<T>) -> () -> T, $@convention(method) <τ_0_0> (@guaranteed ClassWithDefault<τ_0_0>) -> @out τ_0_0
247+
// CHECK: return
248+
249+
// CHECK-LABEL: sil_witness_table hidden ConformsToSillyDefault: SillyDefault module protocol_with_superclass {
250+
// CHECK-NEXT: method #SillyDefault.makeT!1: <Self where Self : SillyDefault> (Self) -> () -> Int : @$S24protocol_with_superclass22ConformsToSillyDefaultCAA0fG0A2aDP5makeTSiyFTW
251+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)