OS versus Swift availability

(Adrian Zubarev) #1

To be crystal clear with everyone, I did read the blog post and followed the related threads but I'm still not sure I did fully understand the impact of ABI so can you clarify this for me once and for all with the current API as an example. (I do not want to derail this topic to a topic about ABI.)

Assuming this proposal is accepted and shipped with Swift X on OS Y which is higher than an OS which declared ABI stability with Swift 5. What does it mean for me if I *want* update my project to Swift X but my minimal deployment target is set to an OS lower than the one where Swift 5 was introduced? How will this work with backwards deployment and at which OS ranges can I use the new API when compiling the project with Swift X?

What availability annotation will be added?

@available(macOS Y, iOS Y, tvOS Y, watchOS Y, *)


@available(swift X)

or both?

Re-pitch: `ContiguousCollection`
(Ben Cohen) #2

Short rule of thumb: @available for someOS X is mandatory, and is about ABI stability. @available for swift Y is optional, and is about source stability. The Swift version is optional because you only need to use it in narrow cases, such as when mitigating source-breaking changes that you only want to kick in when a user updates to a specific version of Swift. In most cases, like additive changes, you don't need to do this and you want to make the feature available to every swift version immediately.


This seems wrong to me, and more importantly it seems unsustainable. I know we’re getting off-topic here, but I really think OS-availability guarantees should only be used for OS-specific features, not for Swift language features.

We already have OS availability tests for macOS, iOS, tvOS, and watchOS. One can expect similar tests for all the multitudinous flavors of Linux, and Windows, and Android, and other operating systems, including future OSes that don’t exist yet on platforms which haven’t been invented.

It does not make sense to list every single OS on every single declaration added to the standard library.

Again, if we’re gating on capabilities of the OS itself, then these tests are probably unavoidable. But if we are gating on capabilities of the Swift runtime or standard library, then we really really ought to be using a single test for exactly that, which works on all platforms.

(Ben Cohen) #4

I moved this over to a new topic, since it isn't directly related to the evolution pitch it was on.

There are two different things involved here:

  • whether standard library features are OS features; and
  • how you refer to those OSs

The first point is inevitable once an OS ships the standard library. When that happens, standard library features are OS features. If a function is not there on the OS you are running on, and you aren't bundling it in your app but relying on it being present in the OS, you simply cannot call it.

The second point has the potential for a future feature: if we could create some kind of "type alias for multiple OS versions", then that could be used in multiple places to avoid manually typing out all of the OS versions that a new feature is going to appear in over and over again. So we could create a standardLibrary 5.0 version alias which when used with @available would imply all the OS versions that version of the standard library is known to ship in. This is certainly less verbose and will avoid errors when implementing the standard library, especially as you note if the number of OSs that might ship a stable standard library increases.

@Michael_Ilseman has been thinking of possible implementations for the second part that we could adopt in a future Swift release. For now, we must specify each OS version individually.

(Jordan Rose) #5

It's worth noting that the existing @available(swift Y) syntax remains useful if we introduce a -swift-version 5.1, or a future -swift-version 7 or something, if a new API would otherwise break source compatibility. So we shouldn't reuse that syntax to expand to a set of OSs.

1 Like

Maybe this is the wrong place to comment, but it seems to me that tying Swift versions to OS versions is going to create a time lag in developer adoption of new Swift versions. Most developers try to support the current and previous versions of macOS (as a minimum). I understand that we can use @available to work around this, but I wonder if that is a realistic option at a language level (vs. api level). I don't see how that would have been possible, realistically, with previous updates (Swift 2 to 3, or Swift 3 to 4). Is there some guarantee that future changes will be far more limited than previous changes? Is there something else that I'm missing?

(Chris Lattner) #7

I posted this on the other thread, moving here since this seems like the preferred discussion point:

Have you considered the approach of adding availability info to the standard library that indicates what swift version the standard library symbol corresponds to, then have an apple-driven mapping that maps Apple OS deployment targets to Swift versions?

The notion of symbol availability was originally designed to be relevant to the library in question (e.g. Swift NIO should have availability markup relative to its release schedule), and then something like a SwiftPM package or other external manifest was intended to map that to OSs and other shipping vehicles.

I don't think this infrastructure ever got built, but it is the logically correct thing to do. We don't want the Swift standard library to have 100 different linux distros on every symbol...

(Ben Cohen) #8

This is very much the minimum we need to unblock landing changes safely today. I think everyone is open to cleaner ways of doing it going forward, including being able to define your own availability that can be mapped onto things like specific OSs.

It could also go beyond symbol availability. For example, it would be useful to define an availability for "Unicode", that mapped onto when a particular release of an OS supported a particular version of Unicode.

(Chris Lattner) #9

How would a "unicode" gate be used in practice? The idea of availability is very much tied to symbol availability and linkage, not API behavior. API behavior is a very different can of worms that is far more complicated.

Swift's language availability is really about the idea that modules created by library developer "A" can be versioned and migrated independently of clients that use them, and that client "B" may want to conditionally depend on features that "A" developed on over time.

This is important for ABI reasons, but also source level reasons. If I'm an app developer and I'm incorporating code (in either source or binary form) from libraries A and B, I may want to have flexibility on how and when I upgrade those dependencies (e.g. C may depend on a specific version of A).

The fact that Apple will end up shipping some snapshot of libraries (potentially cross cutting far more than the Swift standard library) doesn't remove the level of abstraction that packages should be thinking about. From this lens, the Swift standard library is exactly equivalent to any other library in the Swift ecosystem. To pick a silly example, if Apple wanted to ship the open source DogCowJSONPackage in iOS27, it wouldn't make sense to add apple availability to DogCowJSONPackage.

I'd really love to see a mechanism be developed to prevent Apple OS version numbers from entering the standard library. This approach makes sense for Apple system libraries that are inherently and only tied to the OS, but doesn't make sense for a community driven library that will eventually ship on tons of OS's from lots of different vendors - again, imagine if ABI stability comes to linux, and distros start shipping swift by default.


(Ben Cohen) #10

I agree, it would be a great to add a mechanism in future versions of Swift to decouple having to use specific OS versions when checking library availability.