Skip to content

Commit 6f86e24

Browse files
committed
[CodeCompletion] Use Self type in extension for generic substitution when doing implicit member lookup on protocol type
This fixes the regression introduced in #75068 (comment).
1 parent 9906199 commit 6f86e24

4 files changed

+36
-7
lines changed

lib/IDE/CompletionLookup.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,35 @@ Type CompletionLookup::getTypeOfMember(const ValueDecl *VD, Type ExprType) {
720720
if (MaybeNominalType->hasUnboundGenericType())
721721
return T;
722722

723+
// If we are doing implicit member lookup on a protocol and we have found
724+
// a declaration in an extension, use the extension's `Self` type for the
725+
// generic substitution.
726+
// Eg in the following, the `Self` type returned by `qux` is
727+
// `MyGeneric<Int>`, not `MyProto` because of the `Self` type restriction.
728+
// ```
729+
// protocol MyProto {}
730+
// struct MyGeneric<T>: MyProto {}
731+
// extension MyProto where Self == MyGeneric<Int> {
732+
// static func qux() -> Self { .init() }
733+
// }
734+
// func takeMyProto(_: any MyProto) {}
735+
// func test() {
736+
// takeMyProto(.#^COMPLETE^#)
737+
// }
738+
// ```
739+
if (MaybeNominalType->isExistentialType()) {
740+
Type SelfType;
741+
if (auto ED = dyn_cast<ExtensionDecl>(VD->getDeclContext())) {
742+
SelfType = ED->getGenericSignature()->getConcreteType(
743+
ED->getSelfInterfaceType());
744+
}
745+
if (SelfType) {
746+
MaybeNominalType = SelfType;
747+
} else {
748+
return T;
749+
}
750+
}
751+
723752
// For everything else, substitute in the base type.
724753
auto Subs = MaybeNominalType->getMemberSubstitutionMap(VD);
725754

test/IDE/complete_from_stdlib.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func protocolExtCollection1b(_ a: Collection) {
2828
}
2929

3030
// FIXME(https://github.com/apple/swift/issues/65696): We should not be showing this because (1) it cannot be accessed on the existential (2) we don't have the syntax and features to represent the projected type sig anyway.
31-
// PRIVATE_NOMINAL_MEMBERS_2B-DAG: map({#(transform): (any Collection.Element) throws(Error) -> T##(any Collection.Element) throws(Error) -> T#})[' throws'][#[T]#]{{; name=.+}}
31+
// PRIVATE_NOMINAL_MEMBERS_2B-DAG: map({#(transform): (Collection.Element) throws(Error) -> T##(Collection.Element) throws(Error) -> T#})[' throws'][#[T]#]{{; name=.+}}
3232
// NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2B-NOT: Decl{{.*}}: index({#before: any Comparable#})
3333

3434
func protocolExtCollection2<C : Collection where C.Index : BidirectionalIndex>(_ a: C) {

test/IDE/complete_rdar129024996.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ extension P where Self == B<A> {
4646
func bar() {}
4747
// COMPLETE: Begin completions, 2 items
4848
// COMPLETE-DAG: Decl[StaticMethod]/Super/TypeRelation[Convertible]: foo()[#S#]; name=foo()
49-
// COMPLETE-DAG: Decl[StaticMethod]/Super/Flair[ExprSpecific]/TypeRelation[Convertible]: qux()[#any Q#]; name=qux()
49+
// COMPLETE-DAG: Decl[StaticMethod]/Super/TypeRelation[Convertible]: qux()[#B<S>#]; name=qux()

test/IDE/complete_value_expr.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -1523,8 +1523,8 @@ func testUnusableProtExt(_ x: PWithT) {
15231523
x.#^PROTOCOL_EXT_UNUSABLE_EXISTENTIAL^#
15241524
}
15251525
// FIXME(https://github.com/apple/swift/issues/65696): We should not be showing these because (1) they cannot be accessed on the existential (2) we don't have the syntax and features to represent the projected type sigs anyway.
1526-
// PROTOCOL_EXT_UNUSABLE_EXISTENTIAL: Decl[InstanceMethod]/CurrNominal: foo({#(x): any PWithT.T#})[#any PWithT.T#]{{; name=.+}}
1527-
// PROTOCOL_EXT_UNUSABLE_EXISTENTIAL: Decl[InstanceMethod]/CurrNominal: bar({#(x): any PWithT.T#})[#any PWithT.T#]{{; name=.+}}
1526+
// PROTOCOL_EXT_UNUSABLE_EXISTENTIAL: Decl[InstanceMethod]/CurrNominal: foo({#(x): PWithT.T#})[#PWithT.T#]{{; name=.+}}
1527+
// PROTOCOL_EXT_UNUSABLE_EXISTENTIAL: Decl[InstanceMethod]/CurrNominal: bar({#(x): PWithT.T#})[#PWithT.T#]{{; name=.+}}
15281528
15291529
protocol dedupP {
15301530
associatedtype T
@@ -1559,10 +1559,10 @@ func testDeDuped(_ x: dedupS) {
15591559
func testDeDuped2(_ x: dedupP) {
15601560
x#^PROTOCOL_EXT_DEDUP_2^#
15611561
// PROTOCOL_EXT_DEDUP_2: Begin completions, 5 items
1562-
// PROTOCOL_EXT_DEDUP_2-DAG: Decl[InstanceMethod]/CurrNominal: .foo()[#any dedupP.T#]; name=foo()
1563-
// PROTOCOL_EXT_DEDUP_2-DAG: Decl[InstanceVar]/CurrNominal: .bar[#any dedupP.T#]; name=bar
1562+
// PROTOCOL_EXT_DEDUP_2-DAG: Decl[InstanceMethod]/CurrNominal: .foo()[#dedupP.T#]; name=foo()
1563+
// PROTOCOL_EXT_DEDUP_2-DAG: Decl[InstanceVar]/CurrNominal: .bar[#dedupP.T#]; name=bar
15641564
// FIXME(https://github.com/apple/swift/issues/65696): We should not be showing this because (1) it cannot be accessed on the existential (2) we don't have the syntax and features to represent the projected type sig anyway.
1565-
// PROTOCOL_EXT_DEDUP_2-DAG: Decl[Subscript]/CurrNominal: [{#(x): any dedupP.T#}][#any dedupP.T#]; name=[:]
1565+
// PROTOCOL_EXT_DEDUP_2-DAG: Decl[Subscript]/CurrNominal: [{#(x): dedupP.T#}][#dedupP.T#]; name=[:]
15661566
// PROTOCOL_EXT_DEDUP_2-DAG: Keyword[self]/CurrNominal: .self[#any dedupP#]; name=self
15671567
}
15681568
func testDeDuped3<T : dedupP where T.T == Int>(_ x: T) {

0 commit comments

Comments
 (0)