What are the differences between Swift 5 with all upcoming features enabled, and Swift 6?

We have been trying to do an incremental migration to Swift 6, but found that this is seems to be unexpectedly risky. In particular, the way that Swift 6 will crash if a @MainActor function is called off the main actor, combined with the way Swift 5 mode opens up huge loopholes through the @MainActor enforcement and the way Apple APIs are still quite concurrency unaware seems to in practice make it very unwise to migrate parts of your app to Swift 6 while leaving others in Swift 5 mode.

So we are looking at just enabling all upcoming features but leaving everything in Swift 5 mode until the migration is complete, and then re-evaluate if it is safe to turn on Swift 6 mode. The question, however, is:

  • Is this actually viable? Will code compiled in Swift 5 mode with all Swift 6 features enabled actually not crash on wrongly called @MainActor functions?
  • What are the actual differences between Swift 5 with all upcoming features enabled, and Swift 6? Are there any other differences to be aware of?
3 Likes

We've been debating this same question for our Ready for Swift 6 project and are planning to do exactly that, enable all relevant upcoming feature flags in language mode 5, in our next processing run.

It seems like the closest to "what if Swift 6" and I'd be curious to hear if this is a good/bad idea.

1 Like

So far, we've noticed that the Swift 5 compiler is less good at inferring certain things, so going back from Swift 6 -> Swift 5 + all upcoming does introduce some new warnings that need to be manually fixed. Also, in Swift 6 @MainActor is inherently Sendable, while it is not in Swift 5, so it requires adding some more Sendable.

I just waned to note here that the Swift 5 compiler has many less features than the 6 compiler, and is absolutely more difficult to use for concurrency.

However, all of the Swift 6 mode features of the Swift 6 compiler are possible to enable with flags in Swift 5 mode. For example, the sendability differences of globally-isolated closures is controllable via the GlobalActorIsolatedTypesUsability and is super useful if you are using concurrency with Swift 5 mode.