Skip to content

Commit def5d28

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 b1c345f commit def5d28

File tree

3 files changed

+77
-12
lines changed

3 files changed

+77
-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: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,43 @@ 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>'. We only expect unbound generics at the top-level of a type-alias,
1779+
// they are rejected by type resolution in any other position.
1780+
//
1781+
// FIXME: This is a hack – using the declared interface type isn't correct
1782+
// since the generic parameters ought to be introduced at a higher depth,
1783+
// i.e we should be treating it as `typealias X<T> = S<T>`. Ideally this would
1784+
// be fixed by desugaring the unbound typealias during type resolution. For
1785+
// now this is fine though since we only use the resulting type for printing
1786+
// the type annotation; the type relation logic currently skips type
1787+
// parameters.
1788+
if (auto *UGT = underlyingTy->getAs<UnboundGenericType>())
1789+
underlyingTy = UGT->getDecl()->getDeclaredInterfaceType();
1790+
1791+
ASSERT(!underlyingTy->hasUnboundGenericType());
1792+
return underlyingTy;
1793+
}
1794+
17581795
void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
17591796
DeclVisibilityKind Reason,
17601797
DynamicLookupInfo dynamicLookupInfo) {
@@ -1764,18 +1801,7 @@ void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
17641801
Builder.setAssociatedDecl(TAD);
17651802
addLeadingDot(Builder);
17661803
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);
1804+
addTypeAnnotation(Builder, getTypeAliasType(TAD, dynamicLookupInfo));
17791805
}
17801806

17811807
void CompletionLookup::addGenericTypeParamRef(

test/IDE/complete_rdar147789214.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
struct Q<T> {
12+
struct S<U> {}
13+
}
14+
typealias Invalid2 = R<K>.S
15+
16+
let _: S = #^COMPLETE^#
17+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Foo[#S<T>#]; name=Foo
18+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Bar[#S<T>#]; name=Bar
19+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Baz[#S<T>#]; name=Baz
20+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Invalid[#Invalid#]; name=Invalid
21+
// COMPLETE-DAG: Decl[TypeAlias]/CurrModule: Invalid2[#Invalid2#]; name=Invalid2
22+
23+
struct R<U> {
24+
typealias X = S<U>
25+
typealias Y = S
26+
typealias Z<T> = S<T>
27+
28+
func foo() {
29+
// TODO: Once we start comparing type relations for types with generic
30+
// parameters, ideally 'Y' and 'Z' should be convertible, but not 'X'.
31+
let _: S<Int> = #^COMPLETE_IN_TYPE^#
32+
// COMPLETE_IN_TYPE-DAG: Decl[TypeAlias]/CurrNominal: X[#S<U>#]; name=X
33+
// COMPLETE_IN_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Y[#S<T>#]; name=Y
34+
// COMPLETE_IN_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Z[#S<T>#]; name=Z
35+
}
36+
}

0 commit comments

Comments
 (0)