Extensions should inherit availability of thing they are extending

I just encountered an error in this situation:

@available(iOS 13.0, *) class MyClass {
// blah
}

extension MyClass {
// error reported trying to build this in an iOS 12.0 context, because the class is only available in iOS 13.0
}

I'm probably being dumb here, but it seems to me that the extension clause could (and should) safely have an implicit @available(iOS 13.0, *) added to it - in the absence of an explicit one - given that the thing that it is extending has one.

8 Likes

I haven't thought too much about it, but it's not immediately clear to me if that always makes sense. e.g. what if MyClass was also defined in one of the modules you import?

Then the extension isn't extending the version marked @available. If the extension could apply to either, the compiler better complain.

By way of example:

import PlaygroundSupport
import Foundation
import UIKit

@available(iOS 14.0, *) class UIView {}

extension UIView {
    func p() { print(self) }
}

UIView().p()

The above fails to build because UIView is only available in the (fictitious/non-public) iOS 14. IOW, despite import UIKit, the compiler favors the local definition.

If you remove the @available() attribute, you can change the output of p() by commenting out the import.

In short, the compiler is never confused as to which class is being extended.

2 Likes

Okay, I supposed that roughly makes sense, since the compiler still needs to “know” about your redefinition of UIView in order to emit diagnostics, even if it can't be used. It doesn't work like removing the definition entirely would, in terms of shadowing or not shadowing the name.