Skip to content

Commit eeffdef

Browse files
committed
[CSApply] Allow marker existential to superclass conversions
If existential is a protocol composition type where all of the protocols are `@_marker`, narrowly allow coercion to its superclass bound (if it matches). Both types have the same representation which makes it okay. This is only a problem in Swift 5 mode without strict concurrency checks. In this mode `@preconcurrency` stripping happens outside of the solver which means that no conversion restrictions are recorded for members that got `& Sendable` stripped from their types. For example: ```swift struct S { @preconcurrency static let member: KeyPath<String, Int> & Sendable } func test() { _ = S.member } ``` Since `member` is `@preconcurrency` its type would get concurrency annotations stripped, which includes `& Sendable` which means that the solver uses `KeyPath<String, Int>` type for the reference and not the original `KeyPath<String, Int> & Sendable`, this is a problem for `ExprRewritter::adjustTypeForDeclReference` because conversion between existential and its superclass bound requires a constraint restriction which won't be available in this case. Resolves: rdar://132700409 (cherry picked from commit 38aa71d)
1 parent 777dac5 commit eeffdef

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

lib/Sema/CSApply.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7763,6 +7763,20 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
77637763
llvm_unreachable("BuiltinTupleType should not show up here");
77647764
}
77657765

7766+
// Allow existential-to-supertype conversion if all protocol
7767+
// bounds are marker protocols. Normally this requires a
7768+
// conversion restriction but there are situations related
7769+
// to `@preconcurrency` where the `& Sendable` would be stripped
7770+
// transparently to the solver.
7771+
if (auto *existential = fromType->getAs<ExistentialType>()) {
7772+
if (auto *PCT = existential->getConstraintType()
7773+
->getAs<ProtocolCompositionType>()) {
7774+
if (PCT->withoutMarkerProtocols()->isEqual(toType)) {
7775+
return coerceSuperclass(expr, toType);
7776+
}
7777+
}
7778+
}
7779+
77667780
// Unresolved types come up in diagnostics for lvalue and inout types.
77677781
if (fromType->hasUnresolvedType() || toType->hasUnresolvedType())
77687782
return cs.cacheType(new (ctx) UnresolvedTypeConversionExpr(expr, toType));

test/Concurrency/predates_concurrency.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,33 @@ do {
302302
}
303303
}
304304
}
305+
306+
// rdar://132700409 - coercion PartialKeyPath & Sendable -> PartialKeyPath crashes in CSApply
307+
do {
308+
struct Test {
309+
enum KeyPath {
310+
static var member: PartialKeyPath<Test> {
311+
fatalError()
312+
}
313+
}
314+
}
315+
316+
struct KeyPathComparator<Compared> {
317+
@preconcurrency public let keyPath: any PartialKeyPath<Compared> & Sendable
318+
319+
func testDirect() {
320+
switch keyPath { // Ok
321+
case Test.KeyPath.member: break // Ok
322+
default: break
323+
}
324+
}
325+
326+
func testErasure() {
327+
let kp: PartialKeyPath<Compared> = keyPath
328+
switch kp { // Ok
329+
case Test.KeyPath.member: break // Ok
330+
default: break
331+
}
332+
}
333+
}
334+
}

0 commit comments

Comments
 (0)