Skip to content

Commit 38aa71d

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
1 parent fd9a3c9 commit 38aa71d

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
@@ -7806,6 +7806,20 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
78067806
llvm_unreachable("BuiltinTupleType should not show up here");
78077807
}
78087808

7809+
// Allow existential-to-supertype conversion if all protocol
7810+
// bounds are marker protocols. Normally this requires a
7811+
// conversion restriction but there are situations related
7812+
// to `@preconcurrency` where the `& Sendable` would be stripped
7813+
// transparently to the solver.
7814+
if (auto *existential = fromType->getAs<ExistentialType>()) {
7815+
if (auto *PCT = existential->getConstraintType()
7816+
->getAs<ProtocolCompositionType>()) {
7817+
if (PCT->withoutMarkerProtocols()->isEqual(toType)) {
7818+
return coerceSuperclass(expr, toType);
7819+
}
7820+
}
7821+
}
7822+
78097823
// Unresolved types come up in diagnostics for lvalue and inout types.
78107824
if (fromType->hasUnresolvedType() || toType->hasUnresolvedType())
78117825
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)