Skip to content

Fix a bug with inlining self-conformances #20658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ class ModuleDecl : public DeclContext, public TypeDecl {
Optional<ProtocolConformanceRef>
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
/// module.
///
Expand Down
99 changes: 53 additions & 46 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,57 @@ void ModuleDecl::getDisplayDecls(SmallVectorImpl<Decl*> &Results) const {
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>
ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) {
ASTContext &ctx = getASTContext();
Expand Down Expand Up @@ -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())
Expand Down
14 changes: 7 additions & 7 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down