Skip to content

Commit 66e6a6a

Browse files
committed
AST: Fix name lookup for protocols with superclasses
Also, start adding some tests now that basic things seem to work.
1 parent 238aa2d commit 66e6a6a

File tree

4 files changed

+239
-44
lines changed

4 files changed

+239
-44
lines changed

lib/AST/NameLookup.cpp

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

1716+
// Note that we don't have to visit the superclass of a protocol.
1717+
// If we started with an archetype or existential, we'll visit the
1718+
// superclass because we will have added it to the stack upfront.
1719+
//
1720+
// If we started with a concrete class conforming to a protocol
1721+
// with a superclass, we will visit the superclass from the
1722+
// concrete type.
1723+
bool checkProtocolSuperclass = false;
1724+
17161725
// Handle nominal types.
17171726
bool wantProtocolMembers = (options & NL_ProtocolMembers);
17181727
bool wantLookupInAllClasses = false;
17191728
if (auto nominal = type->getAnyNominal()) {
17201729
visited.insert(nominal);
17211730
stack.push_back(nominal);
1731+
1732+
if (isa<ProtocolDecl>(nominal))
1733+
checkProtocolSuperclass = true;
17221734
}
17231735
// Handle archetypes
17241736
else if (auto archetypeTy = type->getAs<ArchetypeType>()) {
@@ -1748,9 +1760,9 @@ bool DeclContext::lookupQualified(Type type,
17481760
}
17491761

17501762
if (auto superclass = layout.explicitSuperclass) {
1751-
auto *nominalDecl = superclass->getClassOrBoundGenericClass();
1752-
if (visited.insert(nominalDecl).second)
1753-
stack.push_back(nominalDecl);
1763+
auto *superclassDecl = superclass->getClassOrBoundGenericClass();
1764+
if (visited.insert(superclassDecl).second)
1765+
stack.push_back(superclassDecl);
17541766
}
17551767
} else {
17561768
llvm_unreachable("Bad type for qualified lookup");
@@ -1849,18 +1861,21 @@ bool DeclContext::lookupQualified(Type type,
18491861
}
18501862
}
18511863

1852-
if (auto protocolDecl = dyn_cast<ProtocolDecl>(current)) {
1853-
if (auto superclassDecl = protocolDecl->getSuperclassDecl())
1854-
if (visited.insert(superclassDecl).second)
1855-
stack.push_back(superclassDecl);
1856-
}
1857-
18581864
// If we're not looking at a protocol and we're not supposed to
18591865
// visit the protocols that this type conforms to, skip the next
18601866
// step.
18611867
if (!wantProtocolMembers && !currentIsProtocol)
18621868
continue;
18631869

1870+
if (checkProtocolSuperclass) {
1871+
if (auto *protoDecl = dyn_cast<ProtocolDecl>(current)) {
1872+
if (auto superclassDecl = protoDecl->getSuperclassDecl()) {
1873+
visited.insert(superclassDecl);
1874+
stack.push_back(superclassDecl);
1875+
}
1876+
}
1877+
}
1878+
18641879
SmallVector<ProtocolDecl *, 4> protocols;
18651880
for (auto proto : current->getAllProtocols()) {
18661881
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
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// RUN: %target-typecheck-verify-swift
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+
// expected-note@-1 {{protocol requires function 'requirementUsesClassTypes' with type '(Generic<Int>.ConcreteAlias, Generic<Int>.GenericAlias) -> ()' (aka '(String, (Int, Int)) -> ()'); do you want to add a stub?}}
22+
}
23+
24+
func duplicateOverload<T : ProtoRefinesClass>(_: T) {}
25+
// expected-note@-1 {{'duplicateOverload' previously declared here}}
26+
27+
func duplicateOverload<T : ProtoRefinesClass & Generic<Int>>(_: T) {}
28+
// expected-error@-1 {{invalid redeclaration of 'duplicateOverload'}}
29+
// expected-warning@-2 {{redundant superclass constraint 'T' : 'Generic<Int>'}}
30+
// expected-note@-3 {{superclass constraint 'T' : 'Generic<Int>' implied here}}
31+
32+
extension ProtoRefinesClass {
33+
func extensionMethodUsesClassTypes(_ x: ConcreteAlias, _ y: GenericAlias) {
34+
_ = ConcreteAlias.self
35+
_ = GenericAlias.self
36+
37+
concreteMethod(x)
38+
genericMethod(y)
39+
40+
let _: Generic<Int> = self
41+
let _: Concrete = self
42+
let _: BaseProto = self
43+
let _: BaseProto & Generic<Int> = self
44+
let _: BaseProto & Concrete = self
45+
46+
let _: Generic<String> = self
47+
// expected-error@-1 {{cannot convert value of type 'Self' to specified type 'Generic<String>'}}
48+
}
49+
}
50+
51+
func usesProtoRefinesClass1(_ t: ProtoRefinesClass) {
52+
let x: ProtoRefinesClass.ConcreteAlias = "hi"
53+
_ = ProtoRefinesClass.ConcreteAlias.self
54+
55+
t.concreteMethod(x)
56+
57+
let y: ProtoRefinesClass.GenericAlias = (1, 2)
58+
_ = ProtoRefinesClass.GenericAlias.self
59+
60+
t.genericMethod(y)
61+
62+
t.requirementUsesClassTypes(x, y)
63+
64+
let _: Generic<Int> = t
65+
let _: Concrete = t
66+
let _: BaseProto = t
67+
let _: BaseProto & Generic<Int> = t
68+
let _: BaseProto & Concrete = t
69+
70+
let _: Generic<String> = t
71+
// expected-error@-1 {{cannot convert value of type 'ProtoRefinesClass' to specified type 'Generic<String>'}}
72+
}
73+
74+
func usesProtoRefinesClass2<T : ProtoRefinesClass>(_ t: T) {
75+
let x: T.ConcreteAlias = "hi"
76+
_ = T.ConcreteAlias.self
77+
78+
t.concreteMethod(x)
79+
80+
let y: T.GenericAlias = (1, 2)
81+
_ = T.GenericAlias.self
82+
83+
t.genericMethod(y)
84+
85+
t.requirementUsesClassTypes(x, y)
86+
87+
let _: Generic<Int> = t
88+
let _: Concrete = t
89+
let _: BaseProto = t
90+
let _: BaseProto & Generic<Int> = t
91+
let _: BaseProto & Concrete = t
92+
93+
let _: Generic<String> = t
94+
// expected-error@-1 {{cannot convert value of type 'T' to specified type 'Generic<String>'}}
95+
}
96+
97+
class BadConformingClass1 : ProtoRefinesClass {
98+
// expected-error@-1 {{'ProtoRefinesClass' requires that 'BadConformingClass1' inherit from 'Generic<Int>'}}
99+
// expected-note@-2 {{requirement specified as 'Self' : 'Generic<Int>' [with Self = BadConformingClass1]}}
100+
func requirementUsesClassTypes(_: ConcreteAlias, _: GenericAlias) {
101+
// expected-error@-1 {{use of undeclared type 'ConcreteAlias'}}
102+
// expected-error@-2 {{use of undeclared type 'GenericAlias'}}
103+
104+
_ = ConcreteAlias.self
105+
// expected-error@-1 {{use of unresolved identifier 'ConcreteAlias'}}
106+
_ = GenericAlias.self
107+
// expected-error@-1 {{use of unresolved identifier 'GenericAlias'}}
108+
}
109+
}
110+
111+
class BadConformingClass2 : Generic<String>, ProtoRefinesClass {
112+
// expected-error@-1 {{'ProtoRefinesClass' requires that 'BadConformingClass2' inherit from 'Generic<Int>'}}
113+
// expected-note@-2 {{requirement specified as 'Self' : 'Generic<Int>' [with Self = BadConformingClass2]}}
114+
// expected-error@-3 {{type 'BadConformingClass2' does not conform to protocol 'ProtoRefinesClass'}}
115+
func requirementUsesClassTypes(_: ConcreteAlias, _: GenericAlias) {
116+
// expected-note@-1 {{candidate has non-matching type '(BadConformingClass2.ConcreteAlias, BadConformingClass2.GenericAlias) -> ()' (aka '(String, (String, String)) -> ()')}}
117+
_ = ConcreteAlias.self
118+
_ = GenericAlias.self
119+
}
120+
}
121+
122+
class GoodConformingClass : Generic<Int>, ProtoRefinesClass {
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+
func anotherExtensionMethodUsesClassTypes(_ x: ConcreteAlias, _ y: GenericAlias) {
137+
_ = ConcreteAlias.self
138+
_ = GenericAlias.self
139+
140+
concreteMethod(x)
141+
genericMethod(y)
142+
143+
let _: Generic<Int> = self
144+
let _: Concrete = self
145+
let _: BaseProto = self
146+
let _: BaseProto & Generic<Int> = self
147+
let _: BaseProto & Concrete = self
148+
149+
let _: Generic<String> = self
150+
// expected-error@-1 {{cannot convert value of type 'Self' to specified type 'Generic<String>'}}
151+
}
152+
}
153+
154+
func usesProtoRefinesProtoWithClass1(_ t: ProtoRefinesProtoWithClass) {
155+
let x: ProtoRefinesProtoWithClass.ConcreteAlias = "hi"
156+
_ = ProtoRefinesProtoWithClass.ConcreteAlias.self
157+
158+
t.concreteMethod(x)
159+
160+
let y: ProtoRefinesProtoWithClass.GenericAlias = (1, 2)
161+
_ = ProtoRefinesProtoWithClass.GenericAlias.self
162+
163+
t.genericMethod(y)
164+
165+
t.requirementUsesClassTypes(x, y)
166+
167+
let _: Generic<Int> = t
168+
let _: Concrete = t
169+
let _: BaseProto = t
170+
let _: BaseProto & Generic<Int> = t
171+
let _: BaseProto & Concrete = t
172+
173+
let _: Generic<String> = t
174+
// expected-error@-1 {{cannot convert value of type 'ProtoRefinesProtoWithClass' to specified type 'Generic<String>'}}
175+
}
176+
177+
func usesProtoRefinesProtoWithClass2<T : ProtoRefinesProtoWithClass>(_ t: T) {
178+
let x: T.ConcreteAlias = "hi"
179+
_ = T.ConcreteAlias.self
180+
181+
t.concreteMethod(x)
182+
183+
let y: T.GenericAlias = (1, 2)
184+
_ = T.GenericAlias.self
185+
186+
t.genericMethod(y)
187+
188+
t.requirementUsesClassTypes(x, y)
189+
190+
let _: Generic<Int> = t
191+
let _: Concrete = t
192+
let _: BaseProto = t
193+
let _: BaseProto & Generic<Int> = t
194+
let _: BaseProto & Concrete = t
195+
196+
let _: Generic<String> = t
197+
// expected-error@-1 {{cannot convert value of type 'T' to specified type 'Generic<String>'}}
198+
}

test/type/subclass_composition.swift

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -443,27 +443,6 @@ func conformsTo<T1 : P2, T2 : Base<Int> & P2>(
443443
conformsToBaseIntAndP2WithWhereClause(baseAndP2Archetype)
444444
}
445445

446-
//
447-
// Protocols with superclass-constrained Self -- not supported yet.
448-
//
449-
450-
protocol ProtoConstraintsSelfToClass where Self : Base<Int> {}
451-
452-
protocol ProtoRefinesClass : Base<Int> {}
453-
protocol ProtoRefinesClassAndProtocolAlias : BaseIntAndP2 {}
454-
protocol ProtoRefinesClassAndProtocolDirect : Base<Int> & P2 {}
455-
protocol ProtoRefinesClassAndProtocolExpanded : Base<Int>, P2 {}
456-
457-
class ClassConformsToClassProtocolBad1 : ProtoConstraintsSelfToClass {}
458-
// expected-error@-1 {{'ProtoConstraintsSelfToClass' requires that 'ClassConformsToClassProtocolBad1' inherit from 'Base<Int>'}}
459-
// expected-note@-2 {{requirement specified as 'Self' : 'Base<Int>' [with Self = ClassConformsToClassProtocolBad1]}}
460-
class ClassConformsToClassProtocolGood1 : Derived, ProtoConstraintsSelfToClass {}
461-
462-
class ClassConformsToClassProtocolBad2 : ProtoRefinesClass {}
463-
// expected-error@-1 {{'ProtoRefinesClass' requires that 'ClassConformsToClassProtocolBad2' inherit from 'Base<Int>'}}
464-
// expected-note@-2 {{requirement specified as 'Self' : 'Base<Int>' [with Self = ClassConformsToClassProtocolBad2]}}
465-
class ClassConformsToClassProtocolGood2 : Derived, ProtoRefinesClass {}
466-
467446
// Subclass existentials inside inheritance clauses
468447
class CompositionInClassInheritanceClauseAlias : BaseIntAndP2 {
469448
required init(classInit: ()) {

0 commit comments

Comments
 (0)