Skip to content

Commit 0b5e00c

Browse files
committed
[CodeCompletion] Suggest static members on protocol extensions with Self bound in unresolved member lookup
rdar://122758029
1 parent 11ef6e5 commit 0b5e00c

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

lib/Sema/IDETypeCheckingRequests.cpp

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

119+
/// Returns `true` if `ED` is an extension of `PD` 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+
/// `PD`.
125+
static bool isExtensionWithSelfBound(const ExtensionDecl *ED,
126+
ProtocolDecl *PD) {
127+
if (!ED || !PD) {
128+
return false;
129+
}
130+
GenericSignature genericSig = ED->getGenericSignature();
131+
if (ED->getExtendedNominal() != PD) {
132+
return false;
133+
}
134+
Type selfType = genericSig->getConcreteType(ED->getSelfInterfaceType());
135+
if (!selfType) {
136+
return false;
137+
}
138+
if (selfType->is<ExistentialType>()) {
139+
return false;
140+
}
141+
return true;
142+
}
143+
119144
static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
120145
const ExtensionDecl *ED) {
121146
// We can't do anything if the base type has unbound generic parameters.
@@ -131,6 +156,10 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
131156
return true;
132157

133158
GenericSignature genericSig = ED->getGenericSignature();
159+
if (isExtensionWithSelfBound(
160+
ED, dyn_cast_or_null<ProtocolDecl>(BaseTy->getAnyNominal()))) {
161+
return true;
162+
}
134163
auto *module = DC->getParentModule();
135164
SubstitutionMap substMap = BaseTy->getContextSubstitutionMap(
136165
module, ED->getExtendedNominal());
@@ -142,7 +171,10 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
142171

143172
static bool isMemberDeclAppliedInternal(const DeclContext *DC, Type BaseTy,
144173
const ValueDecl *VD) {
145-
if (BaseTy->isExistentialType() && VD->isStatic())
174+
if (BaseTy->isExistentialType() && VD->isStatic() &&
175+
!isExtensionWithSelfBound(
176+
dyn_cast<ExtensionDecl>(VD->getDeclContext()),
177+
dyn_cast_or_null<ProtocolDecl>(BaseTy->getAnyNominal())))
146178
return false;
147179

148180
// 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)