Skip to content

Commit d1bedea

Browse files
author
Nathan Hawes
committed
[IDE][Sema] Fix code completion unreachable/hang in TypeBase::getContextSubtitutions
There are paths from Code completion giving it an UnboundGenericType, which it doesn't expect. This patch: 1) Updates the code completion paths to not attempt substitution on UnboundGenericTypes 2) Updates getContextSubstitutions to assert and break out of the loop when given an unhandled type to avoid hanging release builds if a similar bug occurs in future. Resolves rdar://problem/53959978
1 parent e90298c commit d1bedea

File tree

3 files changed

+48
-4
lines changed

3 files changed

+48
-4
lines changed

lib/AST/Type.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3481,7 +3481,9 @@ TypeBase::getContextSubstitutions(const DeclContext *dc,
34813481
continue;
34823482
}
34833483

3484-
llvm_unreachable("Bad base type");
3484+
// Assert and break to avoid hanging if we get an unexpected baseTy.
3485+
assert(0 && "Bad base type");
3486+
break;
34853487
}
34863488

34873489
while (n > 0) {

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,14 @@ static void lookupDeclsFromProtocolsBeingConformedTo(
410410
continue;
411411

412412
// Skip unsatisfied conditional conformances.
413-
if (Conformance->getConditionalRequirementsIfAvailable() &&
414-
!Module->conformsToProtocol(BaseTy, Proto))
415-
continue;
413+
// We can't check them if this type has an UnboundGenericType or if they
414+
// couldn't be computed, so assume they conform in such cases.
415+
if (!BaseTy->hasUnboundGenericType()) {
416+
if (auto res = Conformance->getConditionalRequirementsIfAvailable()) {
417+
if (!res->empty() && !Module->conformsToProtocol(BaseTy, Proto))
418+
continue;
419+
}
420+
}
416421

417422
DeclVisibilityKind ReasonForThisProtocol;
418423
if (Reason == DeclVisibilityKind::MemberOfCurrentNominal)
@@ -793,6 +798,7 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer {
793798
// don't substitute either.
794799
bool shouldSubst = (Reason != DeclVisibilityKind::DynamicLookup &&
795800
!BaseTy->isAnyObject() && !BaseTy->hasTypeVariable() &&
801+
!BaseTy->hasUnboundGenericType() &&
796802
(BaseTy->getNominalOrBoundGenericNominal() ||
797803
BaseTy->is<ArchetypeType>()) &&
798804
VD->getDeclContext()->isTypeContext());

test/IDE/complete_type.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@
397397
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_ARGS_LOCAL_RETURN | %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES
398398

399399
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_DOT_1 | %FileCheck %s -check-prefix=PROTOCOL_DOT_1
400+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNBOUND_DOT | %FileCheck %s -check-prefix=UNBOUND_DOT
401+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNBOUND_DOT_2 | %FileCheck %s -check-prefix=UNBOUND_DOT_2
400402

401403
//===--- Helper types that are used in this test
402404

@@ -1093,3 +1095,37 @@ func testProtocol() {
10931095
// PROTOCOL_DOT_1-DAG: Keyword/None: Type[#FooProtocol.Type#]; name=Type
10941096
// PROTOCOL_DOT_1: End completions
10951097
}
1098+
1099+
//===---
1100+
//===--- Test we can complete unbound generic types
1101+
//===---
1102+
1103+
public final class Task<Success> {
1104+
public enum Inner {
1105+
public typealias Failure = Int
1106+
case success(Success)
1107+
case failure(Failure)
1108+
}
1109+
}
1110+
extension Task.Inner {
1111+
public init(left error: Failure) {
1112+
fatalError()
1113+
}
1114+
}
1115+
extension Task.Inner.#^UNBOUND_DOT^# {}
1116+
func testUnbound(x: Task.Inner.#^UNBOUND_DOT^#) {}
1117+
// UNBOUND_DOT: Begin completions
1118+
// UNBOUND_DOT-DAG: Decl[TypeAlias]/CurrNominal: Failure[#Int#]; name=Failure
1119+
// UNBOUND_DOT-DAG: Keyword/None: Type[#Task.Inner.Type#]; name=Type
1120+
// UNBOUND_DOT: End completions
1121+
1122+
1123+
protocol MyProtocol {}
1124+
struct OuterStruct<U> {
1125+
class Inner<V>: MyProtocol {}
1126+
}
1127+
1128+
func testUnbound2(x: OuterStruct<Int>.Inner.#^UNBOUND_DOT_2^#) {}
1129+
// UNBOUND_DOT_2: Begin completions
1130+
// UNBOUND_DOT_2-DAG: Keyword/None: Type[#OuterStruct<Int>.Inner.Type#]; name=Type
1131+
// UNBOUND_DOT_2: End completions

0 commit comments

Comments
 (0)