Skip to content

Commit 58fa840

Browse files
committed
[Completion] Handle unbound generics in typealiases
The underlying type for a typealias can be an unbound generic type, replace these with the bound generic equivalent. This avoids crashing when attempting to compute the type relation (in the future we'll want to open these type parameters for the comparison). rdar://147789214
1 parent c824be3 commit 58fa840

File tree

3 files changed

+69
-12
lines changed

3 files changed

+69
-12
lines changed

include/swift/IDE/CompletionLookup.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
435435
void addNominalTypeRef(const NominalTypeDecl *NTD, DeclVisibilityKind Reason,
436436
DynamicLookupInfo dynamicLookupInfo);
437437

438+
Type getTypeAliasType(const TypeAliasDecl *TAD,
439+
DynamicLookupInfo dynamicLookupInfo);
440+
438441
void addTypeAliasRef(const TypeAliasDecl *TAD, DeclVisibilityKind Reason,
439442
DynamicLookupInfo dynamicLookupInfo);
440443

lib/IDE/CompletionLookup.cpp

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,41 @@ void CompletionLookup::addNominalTypeRef(const NominalTypeDecl *NTD,
17551755
Builder.setTypeContext(expectedTypeContext, CurrDeclContext);
17561756
}
17571757

1758+
Type CompletionLookup::getTypeAliasType(const TypeAliasDecl *TAD,
1759+
DynamicLookupInfo dynamicLookupInfo) {
1760+
// Substitute the base type for a nested typealias if needed.
1761+
auto ty = getTypeOfMember(TAD, dynamicLookupInfo);
1762+
auto *typeAliasTy = dyn_cast<TypeAliasType>(ty.getPointer());
1763+
if (!typeAliasTy)
1764+
return ty;
1765+
1766+
// If the underlying type has an error, prefer to print the full typealias,
1767+
// otherwise get the underlying type.
1768+
Type underlyingTy = typeAliasTy->getSinglyDesugaredType();
1769+
if (underlyingTy->hasError())
1770+
return ty;
1771+
1772+
// The underlying type might be unbound for e.g:
1773+
//
1774+
// struct S<T> {}
1775+
// typealias X = S
1776+
//
1777+
// Introduce type parameters such that we print the underlying type as
1778+
// 'S<T>'.
1779+
return underlyingTy.transformRec([&](TypeBase *ty) -> std::optional<Type> {
1780+
if (!ty->hasUnboundGenericType()) {
1781+
// Skip.
1782+
return ty;
1783+
}
1784+
auto *UGT = dyn_cast<UnboundGenericType>(ty);
1785+
if (!UGT) {
1786+
// Recurse.
1787+
return std::nullopt;
1788+
}
1789+
return UGT->getDecl()->getDeclaredInterfaceType();
1790+
});
1791+
}
1792+
17581793
void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
17591794
DeclVisibilityKind Reason,
17601795
DynamicLookupInfo dynamicLookupInfo) {
@@ -1764,18 +1799,7 @@ void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
17641799
Builder.setAssociatedDecl(TAD);
17651800
addLeadingDot(Builder);
17661801
addValueBaseName(Builder, TAD->getBaseName());
1767-
1768-
// Substitute the base type for a nested typealias if needed.
1769-
auto ty = getTypeOfMember(TAD, dynamicLookupInfo);
1770-
1771-
// If the underlying type has an error, prefer to print the full typealias,
1772-
// otherwise get the underlying type.
1773-
if (auto *TA = dyn_cast<TypeAliasType>(ty.getPointer())) {
1774-
auto underlyingTy = TA->getSinglyDesugaredType();
1775-
if (!underlyingTy->hasError())
1776-
ty = underlyingTy;
1777-
}
1778-
addTypeAnnotation(Builder, ty);
1802+
addTypeAnnotation(Builder, getTypeAliasType(TAD, dynamicLookupInfo));
17791803
}
17801804

17811805
void CompletionLookup::addGenericTypeParamRef(

test/IDE/complete_rdar147789214.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %batch-code-completion
2+
3+
// rdar://147789214 - Make sure we insert generic parameters for underlying type.
4+
5+
struct S<T> {}
6+
typealias Foo = S
7+
typealias Bar = Foo
8+
typealias Baz<T> = S<T>
9+
typealias Invalid = (S, S)
10+
11+
let _: S = #^COMPLETE^#
12+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Foo[#S<T>#]; name=Foo
13+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Bar[#Foo#]; name=Bar
14+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Baz[#S<T>#]; name=Baz
15+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Invalid[#Invalid#]; name=Invalid
16+
17+
struct R<U> {
18+
typealias X = S<U>
19+
typealias Y = S
20+
typealias Z<T> = S<T>
21+
22+
func foo() {
23+
// TODO: Once we start comparing type relations for types with generic
24+
// parameters, ideally 'Y' and 'Z' should be convertible, but not 'X'.
25+
let _: S<Int> = #^COMPLETE_IN_TYPE^#
26+
// COMPLETE_IN_TYPE-DAG: Decl[TypeAlias]/CurrNominal: X[#S<U>#]; name=X
27+
// COMPLETE_IN_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Y[#S<T>#]; name=Y
28+
// COMPLETE_IN_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Z[#S<T>#]; name=Z
29+
}
30+
}

0 commit comments

Comments
 (0)