Fix a bug with inlining self-conformances.

This commit is contained in:
John McCall
2018-11-16 22:49:07 -05:00
parent 5c205b4573
commit cd2b558280
3 changed files with 65 additions and 53 deletions

View File

@@ -385,6 +385,11 @@ public:
Optional<ProtocolConformanceRef> Optional<ProtocolConformanceRef>
lookupConformance(Type type, ProtocolDecl *protocol); lookupConformance(Type type, ProtocolDecl *protocol);
/// Look for the conformance of the given existential type to the given
/// protocol.
Optional<ProtocolConformanceRef>
lookupExistentialConformance(Type type, ProtocolDecl *protocol);
/// Find a member named \p name in \p container that was declared in this /// Find a member named \p name in \p container that was declared in this
/// module. /// module.
/// ///

View File

@@ -570,6 +570,57 @@ void ModuleDecl::getDisplayDecls(SmallVectorImpl<Decl*> &Results) const {
FORWARD(getDisplayDecls, (Results)); FORWARD(getDisplayDecls, (Results));
} }
Optional<ProtocolConformanceRef>
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<ProtocolConformanceRef> Optional<ProtocolConformanceRef>
ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) {
ASTContext &ctx = getASTContext(); 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 // An existential conforms to a protocol if the protocol is listed in the
// existential's list of conformances and the existential conforms to // existential's list of conformances and the existential conforms to
// itself. // itself.
if (type->isExistentialType()) { if (type->isExistentialType())
// If the existential type cannot be represented or the protocol does not return lookupExistentialConformance(type, protocol);
// 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;
}
// Type variables have trivial conformances. // Type variables have trivial conformances.
if (type->isTypeVariableOrMember()) if (type->isTypeVariableOrMember())

View File

@@ -112,12 +112,6 @@ ProtocolConformanceRef::subst(Type origType,
if (substType->isOpenedExistential()) if (substType->isOpenedExistential())
return *this; 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(); auto *proto = getRequirement();
// Check the conformance map. // Check the conformance map.
@@ -126,7 +120,13 @@ ProtocolConformanceRef::subst(Type origType,
return *result; 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 Type