Skip to content

Commit f2aaf41

Browse files
committed
Make typealiases in protocols available for nested type lookup.
This involved several complications, mostly making the distinction between typealiases whose definitions contain associated types and those which don't: - Only check final component of compound idents for existential-typeness, so that it is legal to refer to a nested typealias inside the existential. - New diagnosis for trying to use typealias which is an alias for an associated type outside of the defining protocol. - Add check on member lookup to omit typealiases to assoc types, similar to existing treatment of assoc types. - Added test.
1 parent 4614adc commit f2aaf41

File tree

4 files changed

+47
-9
lines changed

4 files changed

+47
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,9 @@ NOTE(optional_req_near_match_accessibility,none,
13401340
// Protocols and existentials
13411341
ERROR(assoc_type_outside_of_protocol,none,
13421342
"cannot use associated type %0 outside of its protocol", (Identifier))
1343+
ERROR(typealias_to_assoc_type_outside_of_protocol,none,
1344+
"cannot use typealias %0 of associated type %1 outside of its protocol",
1345+
(Identifier, TypeLoc))
13431346

13441347
ERROR(circular_protocol_def,none,
13451348
"circular protocol inheritance %0", (StringRef))

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -362,17 +362,22 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc,
362362
}
363363
}
364364

365-
// Ignore typealiases found in protocol members.
366-
if (auto alias = dyn_cast<TypeAliasDecl>(typeDecl)) {
367-
auto aliasParent = alias->getParent();
368-
if (aliasParent != dc && isa<ProtocolDecl>(aliasParent))
369-
continue;
370-
}
371-
372365
// Substitute the base into the member's type.
373366
if (Type memberType = substMemberTypeWithBase(dc->getParentModule(),
374367
typeDecl, type,
375368
/*isTypeReference=*/true)) {
369+
370+
// Similar to the associated type case, ignore typealiases containing
371+
// associated types when looking into a non-protocol.
372+
if (auto alias = dyn_cast<TypeAliasDecl>(typeDecl)) {
373+
auto parentProtocol = alias->getDeclContext()->
374+
getAsProtocolOrProtocolExtensionContext();
375+
if (parentProtocol && memberType->hasTypeParameter() &&
376+
!type->is<ArchetypeType>() && !type->isExistentialType()) {
377+
continue;
378+
}
379+
}
380+
376381
// If we haven't seen this type result yet, add it to the result set.
377382
if (types.insert(memberType->getCanonicalType()).second)
378383
result.Results.push_back({typeDecl, memberType});

lib/Sema/TypeCheckType.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,13 +1110,22 @@ static Type resolveNestedIdentTypeComponent(
11101110
member = memberTypes.back().first;
11111111
}
11121112

1113-
if (parentTy->isExistentialType()) {
1113+
if (parentTy->isExistentialType() && isa<AssociatedTypeDecl>(member)) {
11141114
if (diagnoseErrors)
11151115
TC.diagnose(comp->getIdLoc(), diag::assoc_type_outside_of_protocol,
11161116
comp->getIdentifier());
11171117

11181118
return ErrorType::get(TC.Context);
11191119
}
1120+
if (auto alias = dyn_cast<TypeAliasDecl>(member)) {
1121+
if (parentTy->isExistentialType() && memberType->hasTypeParameter()) {
1122+
if (diagnoseErrors)
1123+
TC.diagnose(comp->getIdLoc(), diag::typealias_to_assoc_type_outside_of_protocol,
1124+
comp->getIdentifier(), alias->getUnderlyingTypeLoc());
1125+
1126+
return ErrorType::get(TC.Context);
1127+
}
1128+
}
11201129

11211130
// If there are generic arguments, apply them now.
11221131
if (auto genComp = dyn_cast<GenericIdentTypeRepr>(comp))
@@ -3208,10 +3217,18 @@ class UnsupportedProtocolVisitor
32083217
}
32093218

32103219
bool walkToTypeReprPre(TypeRepr *T) {
3220+
if (T->isInvalid())
3221+
return false;
3222+
if (auto compound = dyn_cast<CompoundIdentTypeRepr>(T)) {
3223+
// Only visit the last component to check, because nested typealiases in
3224+
// existentials are okay.
3225+
visit(compound->getComponentRange().back());
3226+
return false;
3227+
}
32113228
visit(T);
32123229
return true;
32133230
}
3214-
3231+
32153232
std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) {
32163233
if (recurseIntoSubstatements) {
32173234
return { true, S };

test/decl/typealias/typealias.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,17 @@ protocol P4 {
246246
func getSelf() -> X
247247
}
248248

249+
// Availability of typealiases in protocols for nested type lookup
250+
protocol P5 {
251+
associatedtype A
252+
typealias T1 = Int
253+
typealias T2 = A
254+
var a: T2 { get }
255+
}
256+
257+
struct T5 : P5 {
258+
var a: P5.T1 // OK
259+
var v2: P5.T2 // expected-error {{cannot use typealias 'T2' of associated type 'A' outside of its protocol}}
260+
}
261+
249262

0 commit comments

Comments
 (0)