Skip to content

Commit a176c07

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

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

lib/Sema/IDETypeCheckingRequests.cpp

Lines changed: 42 additions & 2 deletions
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+
if (ED->getExtendedNominal() != PD) {
131+
return false;
132+
}
133+
GenericSignature genericSig = ED->getGenericSignature();
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.
@@ -130,8 +155,20 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
130155
if (!ED->isConstrainedExtension())
131156
return true;
132157

133-
GenericSignature genericSig = ED->getGenericSignature();
158+
ProtocolDecl *BaseTypeProtocolDecl = nullptr;
159+
if (auto opaqueType = dyn_cast<OpaqueTypeArchetypeType>(BaseTy)) {
160+
if (opaqueType->getConformsTo().size() == 1) {
161+
BaseTypeProtocolDecl = opaqueType->getConformsTo().front();
162+
}
163+
} else {
164+
BaseTypeProtocolDecl = dyn_cast_or_null<ProtocolDecl>(BaseTy->getAnyNominal());
165+
}
166+
167+
if (isExtensionWithSelfBound(ED, BaseTypeProtocolDecl)) {
168+
return true;
169+
}
134170
auto *module = DC->getParentModule();
171+
GenericSignature genericSig = ED->getGenericSignature();
135172
SubstitutionMap substMap = BaseTy->getContextSubstitutionMap(
136173
module, ED->getExtendedNominal());
137174
return checkRequirements(module,
@@ -142,7 +179,10 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy,
142179

143180
static bool isMemberDeclAppliedInternal(const DeclContext *DC, Type BaseTy,
144181
const ValueDecl *VD) {
145-
if (BaseTy->isExistentialType() && VD->isStatic())
182+
if (BaseTy->isExistentialType() && VD->isStatic() &&
183+
!isExtensionWithSelfBound(
184+
dyn_cast<ExtensionDecl>(VD->getDeclContext()),
185+
dyn_cast_or_null<ProtocolDecl>(BaseTy->getAnyNominal())))
146186
return false;
147187

148188
// We can't leak type variables into another constraint system.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
}
28+
29+
func testOpaqueMyProto() -> some MyProto {
30+
return .#^IN_OPAQUE_PROTOCOL_POS?check=ON_MY_PROTO^#
31+
}

0 commit comments

Comments
 (0)