Hi,
I have had discussions with several developers including some in Swift Users about an issue I raised around property shadowing: [SR-6689] Compiler permits more than one property implementation with same name but differing types via protocol extensions · Issue #49238 · apple/swift · GitHub - I am informed it is not a bug, and that I should discuss possible changes to the language here so...
In a nutshell, I am trying to make some nice "convention-like" Swift APIs. This seems to be a nice thing to do, and protocol extensions arguably make this a Swifty way of doing things, as you can use them to provide defaults for "convention based" properties that consumers of your API can ignore unless they need them.
However, things get ugly really quickly due to the combination of type inference and the permitted shadowing of properties with differing types.
Here's a quick example
protocol Action {
static var parent: Action.Type? { get }
}
extension Action {
static var parent: Action.Type? { return nil }
}
class MyAction: Action {
}
So far so good. My framework code can see MyAction.parent
is nil. Now I hand this code over to public developers so they can create their own actions, and they very naturally do this:
class MyAction: Action {
// Oops this is `Action` not `Action?`, incompatible with the protocol and shadows without a compiler error
static var parent = OtherAction.self
}
Then, if they are lucky (depending on what they assign there), they get compiler errors or weird behaviour, because Swift's compiler permits the shadowing of the property that already has a definition in the protocol extension.
The compiler considers MyAction
to conform to Action
but if you access MyAction.parent
explicitly in code you get a different value to whether you access it through a reference to a MyAction
.
The developer can get very confused by this behaviour and the oblique compiler error messages (if they get them). There are two common scenarios that I've seen happen over and over again with myself working on this:
- you return non-optional forms of types when an optional is expected (very easy when using type inference on the property declaration)
and
- the problem of accidentally returning completely different types, which the compiler seems to permit.
I have been told this shadowing is desired / required behaviour. However I consider the current behaviour and compiler output around it to be very problematic for users of the compiler. Imagine getting problems like this in a Swift Playground on iPad ands trying to explain that to someone. Ouch.
I would therefore like to suggest that at the very least some compiler diagnostic messages are added to the effect of "Property 'x' in type 'Y' has a different type from the declaration in the protocol extension of 'P'. Add the explicit type of the property to fix this". - with a Fix-It option if possible.
I will say that I struggle to see, apart from perhaps a current implementation limitation, why there is value to this ability to shadow a property clearly required by a protocol to which the type intends to conform. The compiler assumes the type conforms to the protocol because of the protocol extension, and yet the type can have completely different property type.
If the protocol extension was missing it would fail to comply because it doesn't conform so... ? I am bemused.