Skip to content

Commit 7eded5d

Browse files
committed
Sema: Allow omitting generic parameters when accessing member type with a fully-constrained 'where' clause
Fixes rdar://problem/78960468.
1 parent dde2d4f commit 7eded5d

File tree

5 files changed

+142
-15
lines changed

5 files changed

+142
-15
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: 36 additions & 10 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);
@@ -1646,6 +1655,23 @@ resolveIdentTypeComponent(TypeResolution resolution,
16461655
parentRange.End = nestedComp->getEndLoc();
16471656
}
16481657

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)) {
1666+
1667+
if (!options.contains(TypeResolutionFlags::SilenceErrors)) {
1668+
diagnoseUnboundGenericType(result,
1669+
lastComp->getNameLoc().getBaseNameLoc());
1670+
}
1671+
1672+
return ErrorType::get(result->getASTContext());
1673+
}
1674+
16491675
return result;
16501676
}
16511677

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)