During the implementation of SE-0064 (Referencing Objective-C selector of property getters and setters) I have come across an issue that could be resolved my a minor change to the language and simplify the compiler a lot. I have drafted a proposal below.
Thoughts, comments, especially objections, appreciated.
– Alex
Disallow arbitrary expressions in selectors
Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Alex Hoppen <https://github.com/ahoppen>
Status: Draft
Review manager: TBD
<swift-evolution/0000-arbitrary-expressions-in-selectors.md at arbitrary-expressions-in-selectors · ahoppen/swift-evolution · GitHub
It is currently possible to write arbitrary expressions inside #selector like the following: #selector(callThisFunc().bar). This complicates the implementation of proposals SE-0064 <https://github.com/apple/swift-evolution/blob/master/proposals/0064-property-selectors.md> (Referencing Objective-C selector of property getters and setters) and SE-0062 <https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md> (Referencing Objective-C key-paths) a lot.
This proposal restricts expressions inside selectors to be a sequence of property or method refernces. I believe this will not be a major restrictions since arbitrary expressions in selectors are probably rarely used, have some rough edges and removing them would simplify the compiler.
I propose allowed expressions inside #selector (and once implemented #keyPath) to be a series of instance or class members separated by . and allow disambiguating the last component using as.
class Address: NSObject {
dynamic var street: String
dynamic var town: String
init(street: String, town: String) {
self.street = street
self.town = town
}
}
class Person: NSObject {
dynamic var name: String
dynamic var homeAddress: Address
func workAddress() -> Address {
// ...
}
func workAddress(formatter: AddressFormatter) -> String {
// ...
}
init(name: String, homeAddress: Address) {
self.name = name
self.homeAddress = homeAddress
}
}
let me: Person = ...
The following examples will continue to work:
let _ = #selector(getter: Person.name)
let _ = #selector(getter: me.name)
let _ = #selector(getter: Person.homeAddress.street)
// Could also be written as
let _ = #selector(getter: Address.street)
let _ = #selector(Person.workAddress as () -> Address)
let _ = #selector(Person.workAddress(formatter: ))
I propose removing this kind of selector:
// Should produce selector "street". Note that the method workAddress() is never
// called and its return type only used during type checking
let _ = #selector(getter: me.workAddress().street)
// The above can be rewritten in a cleaner way like the following
let _ = #selector(getter: Address.street)
The proposed way to rewrite the selector elimininates potential confusion about the fact that calling a method inside #selector actually doesn't invoke it.
selector → #selector(selector-modiferopt selector-path)
selector-modifier → getter:
selector-modifier → setter:
selector-path → type-identifier . selector-member-path as-disambiguationopt
selector-path → selector-member-path as-disambiguationopt
selector-member-path → identifier
selector-member-path → unqualified-name
selector-member-path → identifier . selector-member-path
as-disambiguation → as type-identifier
For a further rationale on why arbitrary expressions are no longer possible, see the discussion <[SR-1239] Implement SE-0064: Referencing the Objective-C selector of property getters and setters · Issue #43847 · apple/swift · GitHub; on bugs.swift.org.
<swift-evolution/0000-arbitrary-expressions-in-selectors.md at arbitrary-expressions-in-selectors · ahoppen/swift-evolution · GitHub on existing code
Code that currently uses this feature needs to be rewritten as described in the example above. I believe, however, that the feature is rarely used so it will affect only very little source code and where it is currently used the proposed update is actually more readable.
The only alternative I see is trying to keep the current semantics and implement them for the getter:/setter: selectors.