Skip to content

Commit ed54b94

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 0a92b1c commit ed54b94

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
@@ -3494,7 +3494,9 @@ TypeBase::getContextSubstitutions(const DeclContext *dc,
34943494
continue;
34953495
}
34963496

3497-
llvm_unreachable("Bad base type");
3497+
// Assert and break to avoid hanging if we get an unexpected baseTy.
3498+
assert(0 && "Bad base type");
3499+
break;
34983500
}
34993501

35003502
while (n > 0) {

lib/Sema/LookupVisibleDecls.cpp

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

405405
// Skip unsatisfied conditional conformances.
406-
if (Conformance->getConditionalRequirementsIfAvailable() &&
407-
!Module->conformsToProtocol(BaseTy, Proto))
408-
continue;
406+
// We can't check them if this type has an UnboundGenericType or if they
407+
// couldn't be computed, so assume they conform in such cases.
408+
if (!BaseTy->hasUnboundGenericType()) {
409+
if (auto res = Conformance->getConditionalRequirementsIfAvailable()) {
410+
if (!res->empty() && !Module->conformsToProtocol(BaseTy, Proto))
411+
continue;
412+
}
413+
}
409414

410415
DeclVisibilityKind ReasonForThisProtocol;
411416
if (Reason == DeclVisibilityKind::MemberOfCurrentNominal)
@@ -782,6 +787,7 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer {
782787
// don't substitute either.
783788
bool shouldSubst = (Reason != DeclVisibilityKind::DynamicLookup &&
784789
!BaseTy->isAnyObject() && !BaseTy->hasTypeVariable() &&
790+
!BaseTy->hasUnboundGenericType() &&
785791
(BaseTy->getNominalOrBoundGenericNominal() ||
786792
BaseTy->is<ArchetypeType>()) &&
787793
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)