Skip to content

Commit f0c3861

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 f0c3861

File tree

3 files changed

+79
-12
lines changed

3 files changed

+79
-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: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,45 @@ 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. We only want the direct underlying type,
1768+
// not the full desugared type, since that more faithfully reflects what's
1769+
// written in source.
1770+
Type underlyingTy = typeAliasTy->getSinglyDesugaredType();
1771+
if (underlyingTy->hasError())
1772+
return ty;
1773+
1774+
// The underlying type might be unbound for e.g:
1775+
//
1776+
// struct S<T> {}
1777+
// typealias X = S
1778+
//
1779+
// Introduce type parameters such that we print the underlying type as
1780+
// 'S<T>'. We only expect unbound generics at the top-level of a type-alias,
1781+
// they are rejected by type resolution in any other position.
1782+
//
1783+
// FIXME: This is a hack – using the declared interface type isn't correct
1784+
// since the generic parameters ought to be introduced at a higher depth,
1785+
// i.e we should be treating it as `typealias X<T> = S<T>`. Ideally this would
1786+
// be fixed by desugaring the unbound typealias during type resolution. For
1787+
// now this is fine though since we only use the resulting type for printing
1788+
// the type annotation; the type relation logic currently skips type
1789+
// parameters.
1790+
if (auto *UGT = underlyingTy->getAs<UnboundGenericType>())
1791+
underlyingTy = UGT->getDecl()->getDeclaredInterfaceType();
1792+
1793+
ASSERT(!underlyingTy->hasUnboundGenericType());
1794+
return underlyingTy;
1795+
}
1796+
17581797
void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
17591798
DeclVisibilityKind Reason,
17601799
DynamicLookupInfo dynamicLookupInfo) {
@@ -1764,18 +1803,7 @@ void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
17641803
Builder.setAssociatedDecl(TAD);
17651804
addLeadingDot(Builder);
17661805
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);
1806+
addTypeAnnotation(Builder, getTypeAliasType(TAD, dynamicLookupInfo));
17791807
}
17801808

17811809
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)