Skip to content

Commit f31643b

Browse files
authored
Merge pull request #80343 from hamishknight/out-of-bounds
[Completion] Handle unbound generics in typealiases
2 parents 14147bf + f0c3861 commit f31643b

File tree

3 files changed

+79
-12
lines changed

3 files changed

+79
-12
lines changed

include/swift/IDE/CompletionLookup.h

+3
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

+40-12
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

+36
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)