Skip to content

Commit 3116203

Browse files
authored
Merge pull request #37868 from slavapestov/fully-constrained-typealias-5.5
Sema: Allow omitting generic parameters when accessing member type with a fully-constrained 'where' clause [5.5]
2 parents f780977 + 7eded5d commit 3116203

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
@@ -713,11 +713,7 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
713713
if (const auto boundTy = openerFn(unboundTy))
714714
return boundTy;
715715

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

@@ -915,11 +911,19 @@ Type TypeResolution::applyUnboundGenericArguments(
915911
return BoundGenericType::get(nominalDecl, parentTy, genericArgs);
916912
}
917913

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

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

14981502
auto maybeDiagnoseBadMemberType = [&](TypeDecl *member, Type memberType,
14991503
AssociatedTypeDecl *inferredAssocType) {
1504+
bool hasUnboundOpener = !!resolution.getUnboundTypeOpener();
1505+
15001506
if (options.contains(TypeResolutionFlags::SilenceErrors)) {
1501-
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member)
1507+
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
1508+
hasUnboundOpener)
15021509
!= TypeChecker::UnsupportedMemberTypeAccessKind::None)
15031510
return ErrorType::get(ctx);
15041511
}
15051512

1506-
switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member)) {
1513+
switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
1514+
hasUnboundOpener)) {
15071515
case TypeChecker::UnsupportedMemberTypeAccessKind::None:
15081516
break;
15091517

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

1638-
// All remaining components use qualified lookup.
1667+
if (!options.contains(TypeResolutionFlags::SilenceErrors)) {
1668+
diagnoseUnboundGenericType(result,
1669+
lastComp->getNameLoc().getBaseNameLoc());
1670+
}
16391671

1640-
// Resolve the parent type.
1641-
Type parentTy = resolveIdentTypeComponent(resolution, silParams,
1642-
parentComps);
1643-
if (!parentTy || parentTy->hasError()) return parentTy;
1644-
1645-
SourceRange parentRange(parentComps.front()->getStartLoc(),
1646-
parentComps.back()->getEndLoc());
1672+
return ErrorType::get(result->getASTContext());
1673+
}
16471674

1648-
// Resolve the nested type.
1649-
return resolveNestedIdentTypeComponent(resolution, silParams,
1650-
parentTy, parentRange,
1651-
comp);
1675+
return result;
16521676
}
16531677

16541678
// 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
@@ -842,13 +842,15 @@ enum class UnsupportedMemberTypeAccessKind : uint8_t {
842842
TypeAliasOfUnboundGeneric,
843843
TypeAliasOfExistential,
844844
AssociatedTypeOfUnboundGeneric,
845-
AssociatedTypeOfExistential
845+
AssociatedTypeOfExistential,
846+
NominalTypeOfUnboundGeneric
846847
};
847848

848849
/// Check whether the given declaration can be written as a
849850
/// member of the given base type.
850851
UnsupportedMemberTypeAccessKind
851-
isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl);
852+
isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl,
853+
bool hasUnboundOpener);
852854

853855
/// @}
854856

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)