[Sema] When looking up member types, use associated types as a last resort.

Swift can infer associated types for decls conforming to protocols, but if
there's already an explicit member type available, we can just use that.

We could probably push this deeper (into qualified lookup), but this is
enough to avoid cases of accidental circular conformance checking, where
we need a particular member type to satisfy a parent conformance.

rdar://problem/18576085

Swift SVN r22664
This commit is contained in:
Jordan Rose
2014-10-10 17:22:08 +00:00
parent 81e32160a6
commit c7761d9e62
2 changed files with 41 additions and 14 deletions

View File

@@ -114,6 +114,7 @@ LookupTypeResult TypeChecker::lookupMemberType(Type type, Identifier name,
// Look through the declarations, keeping only the unique type declarations. // Look through the declarations, keeping only the unique type declarations.
llvm::SmallPtrSet<CanType, 4> types; llvm::SmallPtrSet<CanType, 4> types;
SmallVector<AssociatedTypeDecl *, 4> inferredAssociatedTypes;
for (auto decl : decls) { for (auto decl : decls) {
// Ignore non-types found by name lookup. // Ignore non-types found by name lookup.
auto typeDecl = dyn_cast<TypeDecl>(decl); auto typeDecl = dyn_cast<TypeDecl>(decl);
@@ -128,21 +129,10 @@ LookupTypeResult TypeChecker::lookupMemberType(Type type, Identifier name,
// If we found a member of a protocol type when looking into a non-protocol, // If we found a member of a protocol type when looking into a non-protocol,
// non-archetype type, only include this member in the result set if // non-archetype type, only include this member in the result set if
// this member was used as the default definition or otherwise inferred. // this member was used as the default definition or otherwise inferred.
if (auto protocol = dyn_cast<ProtocolDecl>(typeDecl->getDeclContext())) { if (auto assocType = dyn_cast<AssociatedTypeDecl>(typeDecl)) {
if (!type->is<ArchetypeType>() && !type->isExistentialType()) { if (!type->is<ArchetypeType>() && !type->isExistentialType()) {
// If the type does not actually conform to the protocol, skip this inferredAssociatedTypes.push_back(assocType);
// member entirely. continue;
// FIXME: This is an error path. Should we try to recover?
ProtocolConformance *conformance = nullptr;
if (!conformsToProtocol(type, protocol, dc, &conformance) ||
!conformance || !conformance->isComplete())
continue;
// Use the type witness.
auto assocType = cast<AssociatedTypeDecl>(typeDecl);
memberType = conformance->getTypeWitness(assocType, this)
.getReplacement();
assert(memberType && "Missing type witness?");
} }
} }
@@ -162,6 +152,34 @@ LookupTypeResult TypeChecker::lookupMemberType(Type type, Identifier name,
result.Results.push_back({typeDecl, memberType}); result.Results.push_back({typeDecl, memberType});
} }
if (result.Results.empty()) {
// We couldn't find any normal declarations. Let's try inferring
// associated types.
for (AssociatedTypeDecl *assocType : inferredAssociatedTypes) {
// If the type does not actually conform to the protocol, skip this
// member entirely.
// FIXME: The "isComplete()" check here is bogus. It's entirely possible
// that we're in the middle of checking this protocol and just need a
// particular witness.
auto *protocol = cast<ProtocolDecl>(assocType->getDeclContext());
ProtocolConformance *conformance = nullptr;
if (!conformsToProtocol(type, protocol, dc, &conformance) ||
!conformance || !conformance->isComplete()) {
// FIXME: This is an error path. Should we try to recover?
continue;
}
// Use the type witness.
Type memberType =
conformance->getTypeWitness(assocType, this).getReplacement();
assert(memberType && "Missing type witness?");
// If we haven't seen this type result yet, add it to the result set.
if (types.insert(memberType->getCanonicalType()))
result.Results.push_back({assocType, memberType});
}
}
return result; return result;
} }

View File

@@ -0,0 +1,9 @@
// RUN: %swift -parse -primary-file %S/../Inputs/empty.swift %s -verify
struct A: CollectionType {
struct Index: BidirectionalIndexType {}
}
extension A.Index {
// Force validate "A".
}