mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
test/Sema/extension_binding_multi.swift
Normal file
9
test/Sema/extension_binding_multi.swift
Normal 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".
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user