Skip to content

Commit 835fa02

Browse files
committed
[CSBindings] Infer key path root bindings transitively through contextual root variable
Since generic arguments have to match exactly the inference could be extended to transfer bindings through contextual root types if they are sufficiently resolved (not delayed). For example if key path expression is passed as an argument to a parameter like `WritableKeyPath<$Root, $Value>` and `$Root` is not yet bound but has a binding set of `{String}` its bindings are transferable over to resolve the key path which would be ultimately bound to the contextual type.
1 parent 0ec84e7 commit 835fa02

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,9 +457,28 @@ void BindingSet::inferTransitiveBindings(
457457
inferredRootTy = fnType->getParams()[0].getParameterType();
458458
}
459459

460-
if (inferredRootTy && !inferredRootTy->isTypeVariableOrMember())
461-
addBinding(
460+
if (inferredRootTy) {
461+
// If contextual root is not yet resolved, let's try to see if
462+
// there are any bindings in its set. The bindings could be
463+
// transitively used because conversions between generic arguments
464+
// are not allowed.
465+
if (auto *contextualRootVar = inferredRootTy->getAs<TypeVariableType>()) {
466+
auto rootBindings = inferredBindings.find(contextualRootVar);
467+
if (rootBindings != inferredBindings.end()) {
468+
auto &bindings = rootBindings->getSecond();
469+
470+
// Don't infer if root is not yet fully resolved.
471+
if (bindings.isDelayed())
472+
continue;
473+
474+
for (const auto &binding : bindings.Bindings)
475+
addBinding(binding);
476+
}
477+
} else {
478+
addBinding(
462479
binding.withSameSource(inferredRootTy, BindingKind::Exact));
480+
}
481+
}
463482
}
464483
}
465484
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.15 -solver-expression-time-threshold=1 -swift-version 5
2+
3+
// REQUIRES: OS=macosx
4+
5+
import Combine
6+
7+
enum Status {
8+
case up
9+
case down
10+
}
11+
12+
protocol StatusMonitor {
13+
var statusPublisher: AnyPublisher<Status, Never> { get }
14+
}
15+
16+
protocol UIController {}
17+
protocol ControllerProtocol {}
18+
19+
class TestViewController : UIController, ControllerProtocol {
20+
}
21+
22+
class OtherController {
23+
var innerController: (any UIController & ControllerProtocol)? = nil
24+
}
25+
26+
class Test1 {
27+
var monitor: StatusMonitor
28+
29+
var subscriptions: [AnyCancellable] = []
30+
var status: Status? = nil
31+
var statuses: [Status]? = nil
32+
33+
init(monitor: StatusMonitor) {
34+
self.monitor = monitor
35+
}
36+
37+
func simpleMapTest() {
38+
monitor.statusPublisher
39+
.map { $0 }
40+
.assign(to: \.status, on: self) // Ok
41+
.store(in: &subscriptions)
42+
}
43+
44+
func transformationTest() {
45+
monitor.statusPublisher
46+
.map { _ in (0...1).map { _ in .up } }
47+
.assign(to: \.statuses, on: self) // Ok
48+
.store(in: &subscriptions)
49+
}
50+
}
51+
52+
class FilteringTest {
53+
@Published var flag = false
54+
55+
func test(viewController: inout OtherController) {
56+
_ = $flag.filter { !$0 }
57+
.map { _ in TestViewController() }
58+
.first()
59+
.handleEvents(receiveOutput: { _ in
60+
print("event")
61+
})
62+
.assign(to: \.innerController, on: viewController) // Ok
63+
}
64+
}

0 commit comments

Comments
 (0)