Skip to content

Commit e8abf54

Browse files
committed
Sema: Fixes for protocol typealiases
This is a big refactoring of resolveTypeInContext() which makes the function clearer to understand by merging various special cases and generalizing the logic for walking parent and superclass contexts to cover more cases. This improves typealiases in protocols a bit: 1) Previously a typealias in a protocol either had to be concrete, or consist of a single path of member types from Self, eg Self.A.B. Lift this restriction, so we can now write things like protocol Fireworks { associatedtype Exploding typealias Exploder = Exploding -> [Exploding] } 2) Protocol typealiases can now be accessed via qualified lookup on concrete types. Getting this working for unqualified lookup requires further refactorings which will be in a subsequent patch.
1 parent 9823d16 commit e8abf54

20 files changed

+469
-465
lines changed

lib/AST/Type.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2917,15 +2917,8 @@ TypeSubstitutionMap TypeBase::getMemberSubstitutions(const DeclContext *dc) {
29172917
// If the member is part of a protocol or extension thereof, we need
29182918
// to substitute in the type of Self.
29192919
if (dc->getAsProtocolOrProtocolExtensionContext()) {
2920-
// We only substitute into archetypes for now for protocols.
2921-
// FIXME: This seems like an odd restriction. Whatever is depending on
2922-
// this, shouldn't.
2923-
if (!baseTy->is<ArchetypeType>() && isa<ProtocolDecl>(dc))
2924-
return substitutions;
2925-
29262920
// FIXME: This feels painfully inefficient. We're creating a dense map
29272921
// for a single substitution.
2928-
substitutions[dc->getProtocolSelf()->getArchetype()] = baseTy;
29292922
substitutions[dc->getProtocolSelf()->getDeclaredType()
29302923
->getCanonicalType()->castTo<GenericTypeParamType>()]
29312924
= baseTy;

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -351,37 +351,54 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc,
351351
validateDecl(typeDecl);
352352
if (!typeDecl->hasType()) // FIXME: recursion-breaking hack
353353
continue;
354-
355-
// If we found a member of a protocol type when looking into a non-protocol,
356-
// non-archetype type, only include this member in the result set if
357-
// this member was used as the default definition or otherwise inferred.
358-
if (auto assocType = dyn_cast<AssociatedTypeDecl>(typeDecl)) {
359-
if (!type->is<ArchetypeType>() && !type->isExistentialType()) {
360-
inferredAssociatedTypes.push_back(assocType);
361-
continue;
354+
355+
// If we're looking up a member of a protocol, we must take special care.
356+
if (typeDecl->getDeclContext()->getAsProtocolOrProtocolExtensionContext()) {
357+
// We don't allow lookups of an associated type or typealias of an
358+
// existential type, because we have no way to represent such types.
359+
//
360+
// This is diagnosed further on down in resolveNestedIdentTypeComponent().
361+
if (type->isExistentialType()) {
362+
auto memberType = typeDecl->getInterfaceType()->getRValueInstanceType();
363+
364+
if (memberType->hasTypeParameter()) {
365+
// If we haven't seen this type result yet, add it to the result set.
366+
if (types.insert(memberType->getCanonicalType()).second)
367+
result.Results.push_back({typeDecl, memberType});
368+
369+
continue;
370+
}
362371
}
363-
}
364372

365-
// Substitute the base into the member's type.
366-
if (Type memberType = substMemberTypeWithBase(dc->getParentModule(),
367-
typeDecl, type,
368-
/*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()) {
373+
// If we're looking up an associated type of a concrete type,
374+
// record it later for conformance checking; we might find a more
375+
// direct typealias with the same name later.
376+
if (auto assocType = dyn_cast<AssociatedTypeDecl>(typeDecl)) {
377+
if (!type->is<ArchetypeType>()) {
378+
inferredAssociatedTypes.push_back(assocType);
377379
continue;
378380
}
379381
}
380382

381-
// If we haven't seen this type result yet, add it to the result set.
382-
if (types.insert(memberType->getCanonicalType()).second)
383-
result.Results.push_back({typeDecl, memberType});
383+
// We are looking up an associated type of an archetype, or a
384+
// protocol typealias or an archetype or concrete type.
385+
//
386+
// Proceed with the usual path below.
384387
}
388+
389+
// Substitute the base into the member's type.
390+
auto memberType = substMemberTypeWithBase(dc->getParentModule(),
391+
typeDecl, type,
392+
/*isTypeReference=*/true);
393+
394+
// FIXME: It is not clear why this substitution can fail, but the
395+
// standard library won't build without this check.
396+
if (!memberType)
397+
continue;
398+
399+
// If we haven't seen this type result yet, add it to the result set.
400+
if (types.insert(memberType->getCanonicalType()).second)
401+
result.Results.push_back({typeDecl, memberType});
385402
}
386403

387404
if (result.Results.empty()) {

0 commit comments

Comments
 (0)