@@ -70,10 +70,46 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
70
70
71
71
assert (type->isExistentialType ());
72
72
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
+
73
92
// If the existential type cannot be represented or the protocol does not
74
93
// 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
+
76
111
return ProtocolConformanceRef::forInvalid ();
112
+ }
77
113
78
114
if (protocol->isSpecificProtocol (KnownProtocolKind::Copyable)
79
115
&& !ctx.LangOpts .hasFeature (Feature::NoncopyableGenerics)) {
@@ -89,10 +125,7 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
89
125
// existential to an archetype parameter, so for now we restrict this to
90
126
// @objc protocols and marker protocols.
91
127
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 ();
96
129
// There's a specific exception for protocols with self-conforming
97
130
// witness tables, but the existential has to be *exactly* that type.
98
131
// TODO: synthesize witness tables on-demand for protocol compositions
@@ -107,16 +140,8 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
107
140
108
141
// If the existential is class-constrained, the class might conform
109
142
// 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;
120
145
121
146
// Otherwise, the existential might conform abstractly.
122
147
for (auto protoDecl : layout.getProtocols ()) {
0 commit comments