Design Priorities for the Swift 6 Language Mode

In that case this issue might also be interesting!

Is there a list of all breaking changes currently being considered for Swift 6? If not, will it be published any time soon?

4 Likes

We were just talking about global actor inference in another thread, and it was suggested we bring the suggestion over here:

I would like to propose removing all global-actor inference in Swift 6.

Global actor inference has an extremely complex set of rules, including some extremely surprising ones (such as the "property wrappers" one that is the original subject of that thread). It creates "spooky action at a distance" where conforming to a new protocol, or using a new property wrapper, might completely change the global actor of a type (and all its members).

This kind of source-breaking change might be rough on consumers of frameworks like UIKit, but I believe that it will lead to better understanding of the actual actor isolation requirements of the code that people are writing, as well as fewer hidden surprises and gotchas.

1 Like

Given our guidance that source-breaking changes need to be targeted, not sweeping, I think it's fair to say that removing all global actor inference would be exceedingly unlikely.

However, the thread you linked to discusses a specific inference rule and makes particular arguments about why it in particular merits reconsideration even as it's source breaking; perhaps someone who's familiar with those points might enlighten the community about it here, or otherwise those interested can pop over to the other thread to learn more.

Sure, I'm happy to summarize here, since I made the original post. In brief:

This struct is not actor-isolated:

struct MyView: View {
  @State private var x = 0

  var body: some View {
    Text("\(x)")
  }
}

This struct is actor-isolated (to the main actor):

struct MyView: View {
  @StateObject private var model = SomeModel()

  var body: some View {
    Text("\(model.x)")
  }
}

Simply changing @State to @StateObject changed the actor isolation of the entire containing type. It's because struct StateObject is declared @MainActor, but struct State is not. Applying an actor-isolated property wrapper to a property implicitly propagates that isolation up to the containing type, which in turn propagates it to all sibling declarations of the property (including all other properties and functions within the type).

I made a more complete argument in the other thread, so I'll just summarize here. This upward propagation of actor isolation from a property to the containing type is not well justified by SE-0316 where it was introduced, and has been the source of a lot of confusion (to mention just a couple recent examples). It even inhibits the adoption of @MainActor annotations in some places, because the actor-isolation becomes "viral", spreading further than the author intended. Nobody has commented in favor of the current behavior; the confusion and surprise is universal in all comments.

Changing this behavior (no longer propagating the actor isolation to the containing type) would be source-breaking, but there may be ways to mitigate that breakage for particular frameworks (e.g. SwiftUI). But since it's source-breaking, it would need to happen in a new language mode, so Swift 6 would be the right time to do it.

23 Likes

Would it be appropriate to open an evolution proposal now for things that might change in Swift 6? Or should that wait, since we're still firmly in Swift 5.8 development?

1 Like

Concerning data-race safety by default, one thing currently missing in Swift's concurrency support is interop with other systems that handle their own multithreading environment, like Combine, or RxSwift.

For example, both Combine and RxSwift have a way to subscribe to events on the main thread. Getting a closure marked @MainActor is easy, but to be able call it without error or warning, the place calling it also has to be statically @MainActor. Retrofitting that in an environment managing threads dynamically it is not easy. I guess the easiest would probably to have Swift provide something like @unchecked @MainActor, or a MainActor.runInline { ... } that aborts if not already on the main actor. Currently you can probably hack something using @MainActor(unsafe) but it's not its intended use case, and the (unsafe) part of it is supposed to be ignored by Swift 6.

(By the way Task { @MainActor in ... } does not run the code directly but queues it to be run later so it won't work here, and most of the time you are not in an async context so you cannot await anything inline.)

Closures passed to DispatchQueue.main are considered @MainActor so I thought there was already a solution for this problem, but looking at Swift's source code I realised the handling of DispatchQueue.main was just a hack in the compiler. Combine is an Apple technology so if needed I'm sure you could always also add special support for it in the compiler, but something that could be used by third parties would be really useful. I'm sure most people using RxSwift or something similar would like to not to have to migrate out of it to fully migrate to Swift 6 (the library used needing modifications is fine of course).

2 Likes

Yes, it’s fine to make new evolution proposals now.

1 Like

Swift 6 sounds like a good opportunity to do something about the fact that Decimal loses precision unexpectedly when initiated using a literal since all literals with a period in them pass through a binary floating point stage which is not what one expects when specifying a number that is decimal in its nature.

4 Likes

I don't think that would need a whole new language mode. When we have a design and implementation for decimal literals, I would say we should adopt it even in older language modes, since it should have the same behavior as today for binary floats, and the new behavior would be a bug fix for existing code that uses literal syntax with decimal types.

9 Likes

I agree, I would just hate to see it not being implemented because it would be source-breaking due to a "space bar heating" aspect.