Feedback wanted: confusing concurrency diagnostic messages

Hey folks! I'm currently doing a pass over the compiler's concurrency-related diagnostic messages to make them friendlier and more understandable. If there are any specific diagnostics that you struggled to understand or weren't actionable enough, please let me know in this thread. Feel free to link to existing forum topics that discussed how to resolve specific concurrency diagnostics.

I look forward to your feedback!

34 Likes

Just in case you haven’t already seen this suggestion from Matt: https://github.com/apple/swift/issues/74388

7 Likes

For most of them, the problem is that you have to have quite a deep understanding of the concurrency features, to understand what the right solution is. Makes it quite hard for people new to concurrency to pick the solution that's going to work, and I don't really know what to do about that.

One that is often simple to resolve is the "global var of non-Sendable type"; I'd love a fix-it on that to change var to let, so long as the type is Sendable and non-optional.

6 Likes

Here's one about implicit vs explicit async calls I'm currently confused by:

6 Likes

This is great!

Here is my post about potentially erroneous warning from the compiler: Is it incorrect to use Array.forEach with strict concurrency? - #8 by Jon_Shier

Also see in comments there is another case where compiler things there is a throwing function when in fact there are none Is it incorrect to use Array.forEach with strict concurrency? - #8 by Jon_Shier

2 Likes

This one is a little silly Hyphenation difference between "nonisolated" and other forms of isolation diagnostics · Issue #73982 · apple/swift · GitHub

1 Like

This might be "outside" a discussion specifically about Swift (the language itself)… but I've seen a few instances of SwiftUI protocols that (I would think) should conform to execute only on MainActor that currently are not specified for MainActor. This is a little ambiguous right now because it's not completely clear if this MainActor conformance is coming from the SwiftUI team (in a future beta this summer) or if MainActor conformance is never coming and engineers have to defensively code around that. But this might be an appropriate discussion for Apple Developer Forums.

1 Like

Here’s a good one about initializers Bad diagnostic when constructor can't initialize isolated property · Issue #74557 · apple/swift · GitHub

2 Likes

I think the diagnostics have been much-improved since Swift 5.10 here. But, I do really like explicit distinction between "actor" and "main actor" since it's such a common thing.

1 Like

Here's another of me just typo'ing while trying to use an isolated parameter and getting very confused about the resulting error. I wouldn't be surprised if this actually really tricky to address, but thought I'd include it just in case.

And another that I personally find confusing about the "implicit" vs "explicit" nature of the asynchronous call in question.

I think this is a wonderful one where RBI detecting a pretty subtle issue, but the diagnostics make it hard to understand.

2 Likes

Some things SwiftUI are definitely still buggy regarding the Main Actor isolation.
E.g. SwiftUI.Commands has a nonisolated protocol requirement but has an internal method which is main-actor isolated which produces an error in Swift 6.
As the UI should always run on the main actor, I guess Apple will add the missing isolation annotations in future betas and I would not start working around the warnings/errors (in some cases impossible) just to make the first beta happy.

1 Like

Actor-isolated value of type '() async -> ()' passed as a strongly transferred parameter; later accesses could race
I think some guidance would be helpful for this one. It's unclear what I should do to fix that. I'm also not sure what it means from reading it without also reading a few proposals, but that might be an unrealistic goal.

4 Likes

Oh, thank you for pointing this out! I also don't know what it means for a value to be "strongly" transferred :slightly_smiling_face: I know what "transferring" means, but it's now called "sending", and I don't know what it means for that operation to happen "strongly" or why that would have any implication on what you need to do in source to avoid the risk of a data race.

3 Likes

Yes, concurrency annotations in SwiftUI are outside of the core language, and feedback about the annotations themselves or the lack thereof should be provided via Apple Feedback reports. However, if you're getting any compiler diagnostics that don't make sense that involve the SwiftUI concurrency annotations, please feel free to post specific cases here, as I'm sure the associated compiler error messages could use improvement.

4 Likes

This one can also be a false positive: Swift 6: incorrect compiler error when passing a `sending` closure from a global-actor-isolated function · Issue #74382 · swiftlang/swift · GitHub

Apologies if this is out of scope for your project. Here I'm conforming an actor to a standard protocol.

public protocol FooProtocol {
  var callCount: Int { get set }
}

public actor Foo: FooProtocol {
  public var callCount = 0 // error here
}

Error

Actor-isolated property 'callCount' cannot be used to satisfy nonisolated protocol requirement
Fixit: Add '@preconcurrency' to the 'FooProtocol' conformance to defer isolation checking to run time

The fixit feels like a stopgap solution. My understanding (which could easily be wrong) is that the longer term solution is to have FooProtocol inherit from Actor. It also could just be me, but the use of isolated and nonisolated feels unfamiliar. Better wording might note that the compiler needs to understand when a property has to be accessed asynchronously, and the protocol currently doesn't provide that information.

1 Like

I'm going to start splitting off branches from my current project for better context. Two more errors.
The use of self here is confusing because we're in a function.
Sending 'self'-isolated value of type 'repeat each A' with later accesses to actor-isolated context risks causing data races

A minimal repro case with the same error. I don't understand at all why this isn't allowed. This one could be an odd interaction between parameter packs and actors because I couldn't get it to reproduce without a parameter pack.

actor FooActor<each T> {
  var messages: [(repeat each T)] = []
  
  func message(_ num: repeat each T) {
    messages.append((repeat each num))
  }
}

func foo() async {
  let fooActor = FooActor<Int>()
  await fooActor.message(10)
}

This is from the error above that one in the screenshot. I think we're getting some compiler-internal keywords here or something.

Relevant code: FunctionSpy/Sources/FunctionSpy/FunctionSpy.swift at ErrorExample · twof/FunctionSpy · GitHub
Current toolchain: 6.0-DEVELOPMENT-SNAPSHOT-2024-06-22-a
Let me know if I should upgrade from that one.

2 Likes

Unforutnately, I don't know if adding an Actor conformance to your protocol will actually help in this situation. Synchronous setters and actors don't mix well, so I think you may be just kicking the can down the road to the call sites. Actors are fundamentally asynchronous so they are hard to use with synchronous protocols.

But I agree that more context could help. Another note indicating that you could use a nonisolated computed property could be nice. But, that would prevent you from using actor-isolated state.

I guess the compiler could also suggest a change to the protocol, if it is defined within the same module. But I'm not sure what suggestion would be reasonable...

1 Like

I don't have a lot of experience with parameter packs, but this looks like it should work. I filed a bug about it: Parameter packs seem to interfer with region-based isolation · Issue #74688 · swiftlang/swift · GitHub

1 Like

That looks like a bug rather than a diagnostic message issue. Thank you for the report.

1 Like