Skip to content

Commit a9d0bfb

Browse files
authored
Merge pull request #37859 from slavapestov/fully-constrained-typealias
Sema: Allow omitting generic parameters when accessing member type with a fully-constrained 'where' clause
2 parents 7da5ac7 + e2326b8 commit a9d0bfb

File tree

5 files changed

+159
-34
lines changed

5 files changed

+159
-34
lines changed

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,25 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc,
358358
return result;
359359
}
360360

361+
static bool doesTypeAliasFullyConstrainAllOuterGenericParams(
362+
TypeAliasDecl *aliasDecl) {
363+
auto parentSig = aliasDecl->getDeclContext()->getGenericSignatureOfContext();
364+
auto genericSig = aliasDecl->getGenericSignature();
365+
366+
if (!parentSig || !genericSig)
367+
return false;
368+
369+
for (auto *paramType : parentSig->getGenericParams()) {
370+
if (!genericSig->isConcreteType(paramType))
371+
return false;
372+
}
373+
374+
return true;
375+
}
376+
361377
TypeChecker::UnsupportedMemberTypeAccessKind
362-
TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) {
378+
TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl,
379+
bool hasUnboundOpener) {
363380
// We don't allow lookups of a non-generic typealias of an unbound
364381
// generic type, because we have no way to model such a type in the
365382
// AST.
@@ -376,13 +393,18 @@ TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) {
376393
// underlying type is not dependent.
377394
if (auto *aliasDecl = dyn_cast<TypeAliasDecl>(typeDecl)) {
378395
if (!aliasDecl->isGeneric() &&
379-
aliasDecl->getUnderlyingType()->hasTypeParameter()) {
396+
aliasDecl->getUnderlyingType()->hasTypeParameter() &&
397+
!doesTypeAliasFullyConstrainAllOuterGenericParams(aliasDecl)) {
380398
return UnsupportedMemberTypeAccessKind::TypeAliasOfUnboundGeneric;
381399
}
382400
}
383401

384402
if (isa<AssociatedTypeDecl>(typeDecl))
385403
return UnsupportedMemberTypeAccessKind::AssociatedTypeOfUnboundGeneric;
404+
405+
if (isa<NominalTypeDecl>(typeDecl))
406+
if (!hasUnboundOpener)
407+
return UnsupportedMemberTypeAccessKind::NominalTypeOfUnboundGeneric;
386408
}
387409

388410
if (type->isExistentialType() &&
@@ -433,7 +455,7 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc,
433455
continue;
434456
}
435457

436-
if (isUnsupportedMemberTypeAccess(type, typeDecl)
458+
if (isUnsupportedMemberTypeAccess(type, typeDecl, true)
437459
!= TypeChecker::UnsupportedMemberTypeAccessKind::None) {
438460
auto memberType = typeDecl->getDeclaredInterfaceType();
439461

lib/Sema/TypeCheckType.cpp

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -712,11 +712,7 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
712712
if (const auto boundTy = openerFn(unboundTy))
713713
return boundTy;
714714

715-
// Complain if we're allowed to and bail out with an error.
716-
if (!options.contains(TypeResolutionFlags::SilenceErrors))
717-
diagnoseUnboundGenericType(type, loc);
718-
719-
return ErrorType::get(resolution.getASTContext());
715+
return type;
720716
}
721717
}
722718

@@ -914,11 +910,19 @@ Type TypeResolution::applyUnboundGenericArguments(
914910
return BoundGenericType::get(nominalDecl, parentTy, genericArgs);
915911
}
916912

917-
assert(!resultType->hasTypeParameter());
918-
return resultType;
913+
if (!resultType->hasTypeParameter())
914+
return resultType;
915+
916+
auto parentSig = decl->getDeclContext()->getGenericSignatureOfContext();
917+
if (parentSig) {
918+
for (auto gp : parentSig->getGenericParams())
919+
subs[gp->getCanonicalType()->castTo<GenericTypeParamType>()] =
920+
genericSig->getConcreteType(gp);
921+
}
922+
} else {
923+
subs = parentTy->getContextSubstitutions(decl->getDeclContext());
919924
}
920925

921-
subs = parentTy->getContextSubstitutions(decl->getDeclContext());
922926
skipRequirementsCheck |= parentTy->hasTypeVariable();
923927
} else if (auto genericEnv =
924928
decl->getDeclContext()->getGenericEnvironmentOfContext()) {
@@ -1501,16 +1505,21 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution,
15011505

15021506
auto maybeDiagnoseBadMemberType = [&](TypeDecl *member, Type memberType,
15031507
AssociatedTypeDecl *inferredAssocType) {
1508+
bool hasUnboundOpener = !!resolution.getUnboundTypeOpener();
1509+
15041510
if (options.contains(TypeResolutionFlags::SilenceErrors)) {
1505-
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member)
1511+
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
1512+
hasUnboundOpener)
15061513
!= TypeChecker::UnsupportedMemberTypeAccessKind::None)
15071514
return ErrorType::get(ctx);
15081515
}
15091516

1510-
switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member)) {
1517+
switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
1518+
hasUnboundOpener)) {
15111519
case TypeChecker::UnsupportedMemberTypeAccessKind::None:
15121520
break;
15131521

1522+
case TypeChecker::UnsupportedMemberTypeAccessKind::NominalTypeOfUnboundGeneric:
15141523
case TypeChecker::UnsupportedMemberTypeAccessKind::TypeAliasOfUnboundGeneric:
15151524
case TypeChecker::UnsupportedMemberTypeAccessKind::AssociatedTypeOfUnboundGeneric:
15161525
diagnoseUnboundGenericType(parentTy, parentRange.End);
@@ -1630,29 +1639,44 @@ static Type
16301639
resolveIdentTypeComponent(TypeResolution resolution,
16311640
GenericParamList *silParams,
16321641
ArrayRef<ComponentIdentTypeRepr *> components) {
1633-
auto comp = components.back();
1634-
16351642
// The first component uses unqualified lookup.
1636-
const auto parentComps = components.drop_back();
1637-
if (parentComps.empty()) {
1638-
return resolveTopLevelIdentTypeComponent(resolution, silParams,
1639-
comp);
1640-
}
1643+
auto topLevelComp = components.front();
1644+
auto result = resolveTopLevelIdentTypeComponent(resolution, silParams,
1645+
topLevelComp);
1646+
if (result->hasError())
1647+
return ErrorType::get(result->getASTContext());
1648+
1649+
// Remaining components are resolved via iterated qualified lookups.
1650+
SourceRange parentRange(topLevelComp->getStartLoc(),
1651+
topLevelComp->getEndLoc());
1652+
for (auto nestedComp : components.drop_front()) {
1653+
result = resolveNestedIdentTypeComponent(resolution, silParams,
1654+
result, parentRange,
1655+
nestedComp);
1656+
if (result->hasError())
1657+
return ErrorType::get(result->getASTContext());
1658+
1659+
parentRange.End = nestedComp->getEndLoc();
1660+
}
1661+
1662+
// Diagnose an error if the last component's generic arguments are missing.
1663+
auto lastComp = components.back();
1664+
auto options = resolution.getOptions();
1665+
1666+
if (result->is<UnboundGenericType>() &&
1667+
!isa<GenericIdentTypeRepr>(lastComp) &&
1668+
!resolution.getUnboundTypeOpener() &&
1669+
!options.is(TypeResolverContext::TypeAliasDecl)) {
16411670

1642-
// All remaining components use qualified lookup.
1671+
if (!options.contains(TypeResolutionFlags::SilenceErrors)) {
1672+
diagnoseUnboundGenericType(result,
1673+
lastComp->getNameLoc().getBaseNameLoc());
1674+
}
16431675

1644-
// Resolve the parent type.
1645-
Type parentTy = resolveIdentTypeComponent(resolution, silParams,
1646-
parentComps);
1647-
if (!parentTy || parentTy->hasError()) return parentTy;
1648-
1649-
SourceRange parentRange(parentComps.front()->getStartLoc(),
1650-
parentComps.back()->getEndLoc());
1676+
return ErrorType::get(result->getASTContext());
1677+
}
16511678

1652-
// Resolve the nested type.
1653-
return resolveNestedIdentTypeComponent(resolution, silParams,
1654-
parentTy, parentRange,
1655-
comp);
1679+
return result;
16561680
}
16571681

16581682
// Hack to apply context-specific @escaping to an AST function type.

lib/Sema/TypeChecker.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -846,13 +846,15 @@ enum class UnsupportedMemberTypeAccessKind : uint8_t {
846846
TypeAliasOfUnboundGeneric,
847847
TypeAliasOfExistential,
848848
AssociatedTypeOfUnboundGeneric,
849-
AssociatedTypeOfExistential
849+
AssociatedTypeOfExistential,
850+
NominalTypeOfUnboundGeneric
850851
};
851852

852853
/// Check whether the given declaration can be written as a
853854
/// member of the given base type.
854855
UnsupportedMemberTypeAccessKind
855-
isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl);
856+
isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl,
857+
bool hasUnboundOpener);
856858

857859
/// @}
858860

test/Generics/unbound.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,16 @@ func makeInner() -> Outer<String, String>.Middle.Inner {
9191
var innerProperty: Outer.Middle.Inner = makeInner()
9292
// expected-error@-1 {{reference to generic type 'Outer' requires arguments in <...>}}
9393

94+
// Some nested generic cases
95+
struct OuterStruct<T> { // expected-note 2{{generic type 'OuterStruct' declared here}}
96+
struct InnerStruct<U> {} // expected-note {{generic type 'InnerStruct' declared here}}
97+
}
98+
99+
func nested(_: OuterStruct.InnerStruct) {}
100+
// expected-error@-1 {{reference to generic type 'OuterStruct' requires arguments in <...>}}
101+
102+
func nested(_: OuterStruct.InnerStruct<Int>) {}
103+
// expected-error@-1 {{reference to generic type 'OuterStruct' requires arguments in <...>}}
104+
105+
func nested(_: OuterStruct<Int>.InnerStruct) {}
106+
// expected-error@-1 {{reference to generic type 'OuterStruct<Int>.InnerStruct' requires arguments in <...>}}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
struct OtherGeneric<U> {}
4+
5+
struct Generic<T> {
6+
// FIXME: Should work with 'T' as well
7+
typealias NonGeneric = Int where T == Int
8+
9+
typealias Unbound = OtherGeneric where T == Int
10+
typealias Generic = OtherGeneric where T == Int
11+
}
12+
13+
extension Generic where T == Int {
14+
// FIXME: Should work with 'T' as well
15+
typealias NonGenericInExtension = Int
16+
17+
typealias UnboundInExtension = OtherGeneric
18+
typealias GenericInExtension = OtherGeneric
19+
}
20+
21+
func use(_: Generic.NonGeneric,
22+
_: Generic.Unbound<String>,
23+
_: Generic.Generic<String>,
24+
_: Generic.NonGenericInExtension,
25+
_: Generic.UnboundInExtension<String>,
26+
_: Generic.GenericInExtension<String>) {
27+
28+
// FIXME: Get these working too
29+
#if false
30+
let _ = Generic.NonGeneric.self
31+
let _ = Generic.Unbound<String>.self
32+
let _ = Generic.Generic<String>.self
33+
34+
let _ = Generic.NonGenericInExtension.self
35+
let _ = Generic.UnboundInExtension<String>.self
36+
let _ = Generic.GenericInExtension<String>.self
37+
38+
let _: Generic.NonGeneric = 123
39+
let _: Generic.NonGenericInExtension = 123
40+
#endif
41+
42+
let _: Generic.Unbound = OtherGeneric<String>()
43+
let _: Generic.Generic = OtherGeneric<String>()
44+
45+
let _: Generic.UnboundInExtension = OtherGeneric<String>()
46+
let _: Generic.GenericInExtension = OtherGeneric<String>()
47+
}
48+
49+
struct Use {
50+
let a1: Generic.NonGeneric
51+
let b1: Generic.Unbound<String>
52+
let c1: Generic.Generic<String>
53+
let a2: Generic.NonGenericInExtension
54+
let b2: Generic.UnboundInExtension<String>
55+
let c2: Generic.GenericInExtension<String>
56+
}
57+
58+
extension Generic.NonGeneric {}
59+
extension Generic.Unbound {}
60+
extension Generic.Generic {}
61+
62+
extension Generic.NonGenericInExtension {}
63+
extension Generic.UnboundInExtension {}
64+
extension Generic.GenericInExtension {}

0 commit comments

Comments
 (0)