-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SE-0252][TypeChecker] Keypath Dynamic Member Lookup #23436
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Conversation
I think I can factor out some logic from keypath constraint so I can avoid creating implicit argument expressions, but it's vital to figuring out which overload to use |
9c21387
to
827bcfa
Compare
@swift-ci clean test |
@swift-ci please build toolchain |
@swift-ci please clean test |
Build failed |
Linux Toolchain (Ubuntu 16.04) Install command |
@swift-ci please build toolchain |
Build failed |
@swift-ci please build toolchain |
Linux Toolchain (Ubuntu 16.04) Install command |
macOS Toolchain Install command |
@swift-ci please build toolchain |
Linux Toolchain (Ubuntu 16.04) Install command |
Looks like macOS toolchain job has finished but github missed notification. |
@swift-ci please build macOS toolchain |
Allow `subscript(dynamicMember: {Writeable}KeyPath<...>)` declarations to be found while looking for dynamic members.
Implement keypath based dynamic member lookup which augments functionality of existing @dynamicMemberLookup attribute. Two new subscript overloads are accepted: ``` subscript(dynamicMember member: KeyPath<T, ...>) -> ... subscript(dynamicmember member: WritableKeyPath<T, ...>) -> ... ``` Example: ```swift struct Point { let x: Int var y: Int } @dynamicMemberLookup struct Lens<T> { var obj: T init(_ obj: T) { self.obj = obj } subscript<U>(dynamicMember member: KeyPath<T, U>) -> Lens<U> { get { return Lens<U>(obj[keyPath: member]) } } subscript<U>(dynamicMember member: WritableKeyPath<T, U>) -> Lens<U> { get { return Lens<U>(obj[keyPath: member]) } set { obj[keyPath: member] = newValue.obj } } } var lens = Lens(Point(x: 0, y: 0)) _ = lens.x // converted into `lens[dynamicMember: KeyPath<Point, Int>` _ = lens.y = Lens(10) // converted into `lens[dynamicMember: WritableKeyPath<Point, Int>]` ```
…ing based one Because keypath based choice provides more type information it should be preferred to string based when both solutions are available.
@swift-ci please test source compatibility |
@swift-ci please asan test |
@xedin Did you consider
|
I think this is the option makes most sense, but I don't have test-case in my suite to cover that at the moment, which I'm going to add. |
… be used recursively For example if there a structure which requires multiple implicit dynamic member calls to get the members: ```swift struct Point { var x, y: Int } @dynamicMemberLookup struct Lens<T> { var obj: T subscript<U>(dynamicMember member: KeyPath<T, U>) -> U { get { return obj[keyPath: member] } } } func foo(_ lens: Lens<Lens<Point>>) { _ = lens.x _ = lens.obj.x _ = lens.obj.obj.x } _ = \Lens<Lens<Point>>.x ```
@swift-ci please test |
@swift-ci please test source compatibility |
This option doesn't make sense to me based on the implementation of
The getter does not do recursive dynamic member lookup: it only uses a plain key path subscript. Moreover, I don't see how one can even use compiler magic to synthesize this feature in the general case (i.e., for any type other than |
I think original comment referred to Lens from description which does return another Lens from dynamic lookup method. |
Also even if you consider subscript with returns |
Sorry, I don’t understand. It would be like that if the getter had such an implementation (and could spell such a thing), but the getter is written in plain Swift and returns an ordinary keypath subscript that is not recursively dynamic. |
@xwu Let me try to clarify, dynamic subscript here has a special behavior which tries to use root type fo the KeyPath<T, U> type to lookup members so first lookup would have I have this implemented, let me try to build a toolchain so you can try this out. |
@swift-ci please build toolchain |
Source compatibility failures are related to #23140 |
@swift-ci please ASAN test |
@shahmishal Looks like toolchain reason build completed but there is probably no notification about that fact to github? |
@swift-ci please ASAN test |
@swift-ci please test source compatibility |
Ok, looks like all of the tests are green. I talked to @DougGregor offline and we decided to do a post-merge review of these changes, so I'm going to merge everything. |
Yeah this is where I disagree—or maybe misunderstand. It should not find another dynamic subscript as member. The getter spells out the lookup as using an ordinary key path subscript, not dynamic member lookup. Unless it is your intention that, on Put another way, when |
I am not exactly sure why this has anything to do with Are you trying to say that we shouldn’t support referencing |
Given
In other words, I simply don't see how the getter permits |
@xwu Maybe you should just try debug toolchain and run it with struct Point {
let x, y: Int
}
struct Rectangle {
var topLeft, bottomRight: Point
}
@dynamicMemberLookup
struct Lens<T> {
var obj: T
init(_ obj: T) {
self.obj = obj
}
subscript<U>(dynamicMember member: KeyPath<T, U>) -> U {
get { return obj[keyPath: member] }
}
}
func foo(_ lens: Lens<Lens<Lens<Point>>>) {
print(lens.x)
}
var point = Point(x: 2, y: 3)
foo(Lens(Lens(Lens(point))))
var kp = \Lens<Lens<Point>>.y
var lens = Lens(Lens(point))
print(lens[keyPath: kp]) |
I'm pretty sure I understand how it's working. I don't understand how it can be regarded as consistent with the semantics of the protocol. You are, as you say, making But that is not what the semantics of the protocol demand. Whether |
@xwu That's kind of my point, type-checker doesn't really know how I'm fine saying that looking up member on |
Implement keypath based dynamic member lookup which augments
functionality of existing @dynamicMemberLookup attribute.
Three new subscript overloads are accepted:
Example:
Resolves: rdar://problem/49069813
Resolves: rdar://problem/49533404