Skip to content

Commit 3376d03

Browse files
committed
[MiscDiagnostics] KeyPath KVO diagnostics should handle existential opening
Sendable key path literals are represented as an existential protocol composition with `Sendable` protocol which has to be opened in certain scenarios i.e. to pass it to non-Sendable version.
1 parent 4628987 commit 3376d03

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5449,6 +5449,11 @@ static void maybeDiagnoseCallToKeyValueObserveMethod(const Expr *E,
54495449
auto isKeyPathLiteral = [&](Expr *argExpr) -> KeyPathExpr * {
54505450
if (auto *DTBE = getAsExpr<DerivedToBaseExpr>(argExpr))
54515451
argExpr = DTBE->getSubExpr();
5452+
// Sendable key path literals are represented as an existential
5453+
// protocol composition with `Sendable` protocol which has to be
5454+
// opened in certain scenarios i.e. to pass it to non-Sendable version.
5455+
if (auto *OEE = getAsExpr<OpenExistentialExpr>(argExpr))
5456+
argExpr = OEE->getExistentialValue();
54525457
return getAsExpr<KeyPathExpr>(argExpr);
54535458
};
54545459

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-typecheck-verify-swift -enable-upcoming-feature InferSendableFromCaptures
2+
3+
// REQUIRES: objc_interop
4+
// REQUIRES: concurrency
5+
// REQUIRES: asserts
6+
7+
// This is a copy of test/expr/primary/keypath/keypath-observe-objc.swift with additional requirements to test sendable key paths
8+
9+
import Foundation
10+
11+
class Foo: NSObject {
12+
var number1 = 1
13+
dynamic var number2 = 2
14+
@objc var number3 = 3
15+
@objc dynamic var number4 = 4
16+
@objc var number5: Int {
17+
get { return 5 }
18+
set {}
19+
}
20+
}
21+
22+
class Bar: NSObject {
23+
@objc dynamic let foo: Foo
24+
25+
init(foo: Foo) {
26+
self.foo = foo
27+
super.init()
28+
29+
_ = observe(\.foo.number1, options: [.new]) { _, change in
30+
// expected-warning@-1 {{passing reference to non-'@objc dynamic' property 'number1' to KVO method 'observe(_:options:changeHandler:)' may lead to unexpected behavior or runtime trap}}
31+
print("observer1")
32+
}
33+
34+
_ = observe(\.foo.number2, options: [.new]) { _, change in
35+
// expected-warning@-1 {{passing reference to non-'@objc dynamic' property 'number2' to KVO method 'observe(_:options:changeHandler:)' may lead to unexpected behavior or runtime trap}}
36+
print("observer2")
37+
}
38+
39+
_ = observe(\.foo.number3, options: [.new]) { _, change in
40+
// expected-warning@-1 {{passing reference to non-'@objc dynamic' property 'number3' to KVO method 'observe(_:options:changeHandler:)' may lead to unexpected behavior or runtime trap}}
41+
print("observer3")
42+
}
43+
44+
_ = observe(\.foo.number4, options: [.new]) { _, change in // Okay
45+
print("observer4")
46+
}
47+
48+
_ = observe(\.foo.number5, options: [.new]) { _, change in // Okay
49+
print("observer4")
50+
}
51+
}
52+
}
53+
54+
@_semantics("keypath.mustBeValidForKVO")
55+
func quux<T, V, U>(_ object: T, at keyPath: KeyPath<T, V>, _ keyPath2: KeyPath<T, U>) { }
56+
57+
// The presence of a valid keypath should not prevent detection of invalid ones
58+
// later in the argument list, so start with a valid one here.
59+
quux(Foo(), at: \.number4, \.number1)
60+
// expected-warning@-1 {{passing reference to non-'@objc dynamic' property 'number1' to KVO method 'quux(_:at:_:)' may lead to unexpected behavior or runtime trap}}

0 commit comments

Comments
 (0)