diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 5e4b6b04936..1bfef4b9e8b 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -385,6 +385,11 @@ public: Optional lookupConformance(Type type, ProtocolDecl *protocol); + /// Look for the conformance of the given existential type to the given + /// protocol. + Optional + lookupExistentialConformance(Type type, ProtocolDecl *protocol); + /// Find a member named \p name in \p container that was declared in this /// module. /// diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index e842ef7173d..53c091dd4a3 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -570,6 +570,57 @@ void ModuleDecl::getDisplayDecls(SmallVectorImpl &Results) const { FORWARD(getDisplayDecls, (Results)); } +Optional +ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { + assert(type->isExistentialType()); + + // If the existential type cannot be represented or the protocol does not + // conform to itself, there's no point in looking further. + if (!protocol->existentialConformsToSelf()) + return None; + + auto layout = type->getExistentialLayout(); + + // Due to an IRGen limitation, witness tables cannot be passed from an + // existential to an archetype parameter, so for now we restrict this to + // @objc protocols. + if (!layout.isObjC()) { + return None; + } + + // If the existential is class-constrained, the class might conform + // concretely. + if (auto superclass = layout.explicitSuperclass) { + if (auto result = lookupConformance(superclass, protocol)) + return result; + } + + // Otherwise, the existential might conform abstractly. + for (auto proto : layout.getProtocols()) { + auto *protoDecl = proto->getDecl(); + + // If we found the protocol we're looking for, return an abstract + // conformance to it. + if (protoDecl == protocol) + return ProtocolConformanceRef(protocol); + + // If the protocol has a superclass constraint, we might conform + // concretely. + if (auto superclass = protoDecl->getSuperclass()) { + if (auto result = lookupConformance(superclass, protocol)) + return result; + } + + // Now check refined protocols. + if (protoDecl->inheritsFrom(protocol)) + return ProtocolConformanceRef(protocol); + } + + // We didn't find our protocol in the existential's list; it doesn't + // conform. + return None; +} + Optional ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { ASTContext &ctx = getASTContext(); @@ -609,52 +660,8 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { // An existential conforms to a protocol if the protocol is listed in the // existential's list of conformances and the existential conforms to // itself. - if (type->isExistentialType()) { - // If the existential type cannot be represented or the protocol does not - // conform to itself, there's no point in looking further. - if (!protocol->existentialConformsToSelf()) - return None; - - auto layout = type->getExistentialLayout(); - - // Due to an IRGen limitation, witness tables cannot be passed from an - // existential to an archetype parameter, so for now we restrict this to - // @objc protocols. - if (!layout.isObjC()) - return None; - - // If the existential is class-constrained, the class might conform - // concretely. - if (auto superclass = layout.explicitSuperclass) { - if (auto result = lookupConformance(superclass, protocol)) - return result; - } - - // Otherwise, the existential might conform abstractly. - for (auto proto : layout.getProtocols()) { - auto *protoDecl = proto->getDecl(); - - // If we found the protocol we're looking for, return an abstract - // conformance to it. - if (protoDecl == protocol) - return ProtocolConformanceRef(protocol); - - // If the protocol has a superclass constraint, we might conform - // concretely. - if (auto superclass = protoDecl->getSuperclass()) { - if (auto result = lookupConformance(superclass, protocol)) - return result; - } - - // Now check refined protocols. - if (protoDecl->inheritsFrom(protocol)) - return ProtocolConformanceRef(protocol); - } - - // We didn't find our protocol in the existential's list; it doesn't - // conform. - return None; - } + if (type->isExistentialType()) + return lookupExistentialConformance(type, protocol); // Type variables have trivial conformances. if (type->isTypeVariableOrMember()) diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index a6205e0ad9e..d5d01761196 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -112,12 +112,6 @@ ProtocolConformanceRef::subst(Type origType, if (substType->isOpenedExistential()) return *this; - // If the substituted type is an existential, we have a self-conforming - // existential being substituted in place of itself. There's no - // conformance information in this case, so just return. - if (substType->isObjCExistentialType()) - return *this; - auto *proto = getRequirement(); // Check the conformance map. @@ -126,7 +120,13 @@ ProtocolConformanceRef::subst(Type origType, return *result; } - llvm_unreachable("Invalid conformance substitution"); + // The only remaining case is that the type is an existential that + // self-conforms. + assert(substType->isExistentialType()); + auto optConformance = + proto->getModuleContext()->lookupExistentialConformance(substType, proto); + assert(optConformance && "existential type didn't self-conform"); + return *optConformance; } Type