@@ -70,10 +70,46 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
7070
7171 assert (type->isExistentialType ());
7272
73+ auto getConstraintType = [&type]() {
74+ if (auto *existentialTy = type->getAs <ExistentialType>())
75+ return existentialTy->getConstraintType ();
76+ return type;
77+ };
78+
79+ auto lookupSuperclassConformance = [&](ExistentialLayout &layout) {
80+ if (auto superclass = layout.explicitSuperclass ) {
81+ if (auto result =
82+ lookupConformance (superclass, protocol, /* allowMissing=*/ false )) {
83+ if (protocol->isSpecificProtocol (KnownProtocolKind::Sendable) &&
84+ result.hasUnavailableConformance ())
85+ return ProtocolConformanceRef::forInvalid ();
86+ return result;
87+ }
88+ }
89+ return ProtocolConformanceRef::forInvalid ();
90+ };
91+
7392 // If the existential type cannot be represented or the protocol does not
7493 // conform to itself, there's no point in looking further.
75- if (!protocol->existentialConformsToSelf ())
94+ if (!protocol->existentialConformsToSelf ()) {
95+ // If type is a protocol composition with marker protocols
96+ // check whether superclass conforms, and if it does form
97+ // an inherited conformance. This means that types like:
98+ // `KeyPath<String, Int> & Sendable` don't have to be "opened"
99+ // to satisfy conformance to i.e. `Equatable`.
100+ if (getConstraintType ()->is <ProtocolCompositionType>()) {
101+ auto layout = type->getExistentialLayout ();
102+ if (llvm::all_of (layout.getProtocols (),
103+ [](const auto *P) { return P->isMarkerProtocol (); })) {
104+ if (auto conformance = lookupSuperclassConformance (layout)) {
105+ return ProtocolConformanceRef (
106+ ctx.getInheritedConformance (type, conformance.getConcrete ()));
107+ }
108+ }
109+ }
110+
76111 return ProtocolConformanceRef::forInvalid ();
112+ }
77113
78114 if (protocol->isSpecificProtocol (KnownProtocolKind::Copyable)
79115 && !ctx.LangOpts .hasFeature (Feature::NoncopyableGenerics)) {
@@ -89,10 +125,7 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
89125 // existential to an archetype parameter, so for now we restrict this to
90126 // @objc protocols and marker protocols.
91127 if (!layout.isObjC () && !protocol->isMarkerProtocol ()) {
92- auto constraint = type;
93- if (auto existential = constraint->getAs <ExistentialType>())
94- constraint = existential->getConstraintType ();
95-
128+ auto constraint = getConstraintType ();
96129 // There's a specific exception for protocols with self-conforming
97130 // witness tables, but the existential has to be *exactly* that type.
98131 // TODO: synthesize witness tables on-demand for protocol compositions
@@ -107,16 +140,8 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
107140
108141 // If the existential is class-constrained, the class might conform
109142 // concretely.
110- if (auto superclass = layout.explicitSuperclass ) {
111- if (auto result = lookupConformance (
112- superclass, protocol, /* allowMissing=*/ false )) {
113- if (protocol->isSpecificProtocol (KnownProtocolKind::Sendable) &&
114- result.hasUnavailableConformance ())
115- result = ProtocolConformanceRef::forInvalid ();
116-
117- return result;
118- }
119- }
143+ if (auto conformance = lookupSuperclassConformance (layout))
144+ return conformance;
120145
121146 // Otherwise, the existential might conform abstractly.
122147 for (auto protoDecl : layout.getProtocols ()) {
0 commit comments