Skip to content

Commit f184baa

Browse files
committed
[CodeCompletion] Suggest static members on protocol extensions with Self bound in unresolved member lookup
rdar://122758029
1 parent 9607e9d commit f184baa

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

lib/Sema/IDETypeCheckingRequests.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,29 @@ class ContainsSpecializableArchetype : public TypeWalker {
116116
}
117117
};
118118

119+
/// Returns `true` if `ED` is an extension of `BaseTy` that binds `Self` to a
120+
/// concrete type, like `extension MyProto where Self == MyStruct {}`.
121+
///
122+
/// In these cases, it is possible to access static members defined in the
123+
/// extension when perfoming unresolved member lookup in a type context of
124+
/// `BaseType`.
125+
static bool isExtensionWithSelfBound(const ExtensionDecl *ED, Type BaseTy) {
126+
if (!ED) {
127+
return false;
128+
}
129+
GenericSignature genericSig = ED->getGenericSignature();
130+
if (!ED->getExtendedType()->isEqual(BaseTy)) {
131+
return false;
132+
}
133+
if (!ED->getSelfInterfaceType()->isTypeParameter()) {
134+
return false;
135+
}
136+
if (!genericSig->getConcreteType(ED->getSelfInterfaceType())) {
137+
return false;
138+
}
139+
return true;
140+
}
141+
119142
static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
120143
const ExtensionDecl *ED) {
121144
// We can't do anything if the base type has unbound generic parameters.
@@ -131,6 +154,9 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
131154
return true;
132155

133156
GenericSignature genericSig = ED->getGenericSignature();
157+
if (isExtensionWithSelfBound(ED, BaseTy)) {
158+
return true;
159+
}
134160
auto *module = DC->getParentModule();
135161
SubstitutionMap substMap = BaseTy->getContextSubstitutionMap(
136162
module, ED->getExtendedNominal());
@@ -142,7 +168,9 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
142168

143169
static bool isMemberDeclAppliedInternal(const DeclContext *DC, Type BaseTy,
144170
const ValueDecl *VD) {
145-
if (BaseTy->isExistentialType() && VD->isStatic())
171+
if (BaseTy->isExistentialType() && VD->isStatic() &&
172+
!isExtensionWithSelfBound(dyn_cast<ExtensionDecl>(VD->getDeclContext()),
173+
BaseTy))
146174
return false;
147175

148176
// We can't leak type variables into another constraint system.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %batch-code-completion
2+
3+
protocol MyProto {}
4+
protocol MyOtherProto: MyProto {}
5+
struct MyStruct : MyProto {}
6+
extension MyProto where Self == MyStruct {
7+
static var constrainedOnMyStruct: MyStruct { fatalError() }
8+
}
9+
extension MyProto where Self: MyOtherProto {
10+
static var constrainedonMyInheritanceOfOtherProto: MyOtherProto { fatalError() }
11+
}
12+
extension MyProto where Self == MyOtherProto {
13+
static var constrainedonMyEqualityOfOtherProto: MyOtherProto { fatalError() }
14+
}
15+
16+
17+
func testOnMyProto() {
18+
let _: MyProto = .#^ON_MY_PROTO^#
19+
// ON_MY_PROTO: Begin completions, 1 items
20+
// ON_MY_PROTO-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: constrainedOnMyStruct[#MyStruct#];
21+
}
22+
23+
func testOnMyOtherProto() {
24+
// constrainedOnMyStruct is not valid here
25+
let _: MyOtherProto = .#^ON_MY_OTHER_PROTO^#
26+
// ON_MY_OTHER_PROTO-NOT: Begin completions
27+
}

0 commit comments

Comments
 (0)