swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.51k stars 10.35k forks source link

[SR-4017] It's impossible to add a property setter through extension #46602

Open swift-ci opened 7 years ago

swift-ci commented 7 years ago
Previous ID SR-4017
Radar None
Original Reporter marc (JIRA User)
Type Improvement
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 1 | |Component/s | | |Labels | Improvement, LanguageFeatureRequest | |Assignee | None | |Priority | Medium | md5: 51a30ea6603f4c1be8bc14581c1df11a

Issue Description:

It is currently not possible to add a setter to a computed property through an extension there that property also works properly outside the module (i.e. in other modules who import the extension).

CGRect for example has two read-only computed properties height and width.

Through an extension these properties can be shadowed by read-write computed properties like this:

extension CGRect {

    public var height: CGFloat {
        get { return size.height }
        mutating set { size.height = newValue }
    }

    public var width: CGFloat {
        get { return size.width }
        mutating set { size.width = newValue }
    }
}

and then be used like this:

var rect = CGRect()
rect.height = 1
rect.width = 2

var height = rect.height
var width = rect.width

It works fine in the same module as where the extension was defined.

It does not work however in other modules which import that extension. While the setters work fine, the getters will be considered "ambiguous" (between the original and the extension's implementations) and Swift provides no way to disambiguate this:

import ModuleProvidingTheExtension

var rect = CGRect()
rect.height = 1 // works
rect.width = 2 // works

var height = rect.height // Ambiguous use of 'height'
var width = rect.width // Ambiguous use of 'width'

There is no workaround for this problem.

Possible solutions:

The last solution (providing a setter without getter) would easily fix this issue. It maintains source-compatibility and introduces no new syntax.

Example:

extension CGRect {

    public var height: CGFloat {
        mutating set { size.height = newValue }
    }

    public var width: CGFloat {
        mutating set { size.width = newValue }
    }
}

It adds a setter to the existing read-only properties.
Providing just a setter is only allowed if the property being affected is already defined somewhere - i.e. already has a getter.

This could be extended to allowing subclasses to override only the setter of a property without having to override the getter just to call super's implementation. Same for protocol conformances where the getter would already be covered by a default implementation.

--

I consider this to be more a bug than a feature request because overwriting the property in an extension leads to an unresolvable ambiguous access error in the getter only.

If necessary I'll re-post this on the Swift Evolution Mailing List.

belkadan commented 7 years ago

Yes, this would need to go through the Swift Evolution Process.