Swift 6 language mode being passed to dependencies

Hi all,

We're seeing an issue in Vapor when compiling it with the tools version set to 6.0 and the language mode set to .v6 in that it's failing to compile because of a Sendable warning in a dependency (Swift Metrics). This dependency is currently on 5.6 so should be built in Swift 5 language mode correct?

I think this is a compiler/SwiftPM bug but let me know what the best steps to report are. This is happening on the current Xcode 16 beta. You can reproduce it with the swift-6-check branch on GitHub - vapor/vapor: 💧 A server-side Swift HTTP web framework.

4 Likes

It may be worth trying to reproduce this problem with a command line only build without Xcode or xcodebuild involved.

Xcode 16 beta 1's behavior currently differs from swift in how default languge mode is set. I don't immediately see how that would cause the problem you are seeing happen. But, it at least does sound like it could be related.

Same issue with swift build:

 377 | public struct TimeUnit: Equatable {
     |               `- note: consider making struct 'TimeUnit' conform to the 'Sendable' protocol
 378 |     private enum Code: Equatable {
 379 |         case nanoseconds
     :
 399 |     public static let microseconds = TimeUnit(code: .microseconds, scaleFromNanoseconds: 1000)
 400 |     public static let milliseconds = TimeUnit(code: .milliseconds, scaleFromNanoseconds: 1000 * TimeUnit.microseconds.scaleFromNanoseconds)
 401 |     public static let seconds = TimeUnit(code: .seconds, scaleFromNanoseconds: 1000 * TimeUnit.milliseconds.scaleFromNanoseconds)
     |                       |- error: static property 'seconds' is not concurrency-safe because non-'Sendable' type 'TimeUnit' may have shared mutable state
     |                       |- note: annotate 'seconds' with '@MainActor' if property should only be accessed from the main actor
     |                       `- note: disable concurrency-safety checks if accesses are protected by an external synchronization mechanism
 402 |     public static let minutes = TimeUnit(code: .minutes, scaleFromNanoseconds: 60 * TimeUnit.seconds.scaleFromNanoseconds)
 403 |     public static let hours = TimeUnit(code: .hours, scaleFromNanoseconds: 60 * TimeUnit.minutes.scaleFromNanoseconds)
/Users/timc/Developer/Vapor/vapor/.build/checkouts/swift-metrics/Sources/CoreMetrics/Metrics.swift:401:23: error: static property 'seconds' is not concurrency-safe because non-'Sendable' type 'TimeUnit' may have shared mutable state

Note that the type and declaration producing the error are in a dependency

I spent some time investigating this and turns out this is not a SwiftPM issue but a compiler one. The diagnostic points to the declaration of the seconds but it should actually be pointing to the point-of-use in this case. I'll working on a fix for this and will update this thread once that's done.

8 Likes

Fix for adding the missing Sendable annotations is here

This might explain why we’ve seen an increase in error counts in our “Ready for Swift 6” graph for the Xcode 16b1 build. We’d be counting root dependencies’ errors multiple times.

PR [Concurrency] Don't attempt to diagnose `GlobalConcurrency` issues wi… by xedin · Pull Request #74614 · swiftlang/swift · GitHub addresses this issue.

1 Like

Wonderful thank you!

Just to follow up on this if it's helpful, using @preconcurrency import on the offending library doesn't appear to help

Final one - without meaning to highjack the thread - is InferSendableFromCaptures implemented in the current Swift 6 toolchain? We're seeing an error with code like

    private static let prefix: [String: WritableKeyPath<Self, Int?>] = [
            "max-age": \.maxAge,
            "s-maxage": \.sMaxAge,
            "min-fresh": \.minFresh,
            "stale-while-revalidate": \.staleWhileRevalidate,
            "stale-if-error": \.staleIfError
        ]

I believe this should be fine in Swift 6 with SE-0418 but it doesn't appear to being picked up

I'm not sure what you mean here, the contextual type here is not parked as Sendable which overrides any inference we apply to the literal because that's the type we covert to.

2 Likes

The type of prefix should be [String: WritableKeyPath<Self, Int?> & Sendable].

1 Like

Ahhh thank you!

2 Likes