Silence warnings about safe shared mutable state

I've been trying out the new SWIFT_STRICT_CONCURRENCY flag in Xcode 14 and I've come across the following scenario that I am unsure on how to best resolve. Here's a simplified example:

private var xKey = false

protocol Foo: AnyObject {
    var x: String { get }
}

extension Foo {
    var x: String {
        if let x = objc_getAssociatedObject(self, &xKey) as? String {
            return x
        } else {
            let x = "<more interesting in real life>"
            objc_setAssociatedObject(self, &xKey, x, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            return x
        }
    }
}

At the point of use of &xKey I am getting the following warning:

Reference to var 'xKey' is not concurrency-safe because it involves shared mutable state

What would be the best way to resolve this? Is there any annotation I can use to pinky-swear to the compiler that this is OK?

The only solution I've found so far, which feels heavy, is to define my key like this instead:

private final class Key: @unchecked Sendable {
    var value = false
}

private let xKey = Key()

And then use: &xKey.value.

Just let key = malloc(1)! is fine. No problem of using C call with Objective-C API in a swift app. :grinning:

Hello, thank you! This does not resolve the issue however.

Switching to private let xKey = malloc(1) yields this error:

Cannot pass immutable value as inout argument: 'xKey' is a 'let' constant

And switching to var instead of let puts me back at my original position.

Reference to var 'xKey' is not concurrency-safe because it involves shared mutable state

In this case use xKey as is, without &, that's the whole point.

let key = malloc(1)! // this is global / static / or stored in some singleton class for example
...
objc_getAssociatedObject(self, key)

Got it. Yes, that does resolve the issue, thanks for the help!

I do wonder if this could be generalized to a different case that triggers the same warning though:

I am using the "environment/world" pattern described by the pointfree folks.

struct World: @unchecked Sendable {
    var date: () -> Date

    init() {
        date = Date.init
    }
}

var Current = World()

// elsewhere
func foo() {
    print(Current.date()) // Reference to var 'Current' is not concurrency-safe because it involves shared mutable state
}

Current is a var, but it is effectively a let once configured in app-launch so it is safe, but I'm unsure of how to convince the compiler of that. I've been hoping there was some sort of @unchecked-ish annotation to use for vars.

It would help if you provide the fragment that shows the warning / error, otherwise it is not clear what the issue is.

Updated my example, sorry about that!

Will this work?

struct World: @unchecked Sendable {
    var date: () -> Date

    init() {
        date = Date.init
    }
}

class Current {
    static let current = Current()
    var world: World
    private init() {
        // this init will be called on the first Current.current call
        // configure the world appropriately here
        world = World()
    }
}

// elsewhere
func foo() {
    print(Current.current.world.date())
}

I've come up with this which gives me the same interface as before, like being able to just call Current.date(). Still really wish there was some kind of annotation though to just mark a var is unchecked :thinking:.

struct World: @unchecked Sendable {
    var date: () -> Date

    init() {
        date = Date.init
    }
}

@dynamicMemberLookup
final class WorldContainer<W: Sendable>: @unchecked Sendable {
    var world: W

    init(_ world: W) {
        self.world = world
    }

    subscript<V>(dynamicMember keyPath: KeyPath<W, V>) -> V {
        world[keyPath: keyPath]
    }

    subscript<V>(dynamicMember keyPath: WritableKeyPath<W, V>) -> V {
        get {
            world[keyPath: keyPath]
        }
        set {
            world[keyPath: keyPath] = newValue
        }
    }
}

let Current = WorldContainer(World())

func foo() {
    print(Current.date())
}

I see :thinking:

What prevents you from using let for your Current variable? e.g. like so:

let Current = World.makeWorld()
// makeWorld does the relevant configuration logic.

The pattern is used as a DI solution so it needs to remain a var so that you configure it differently while in unit tests. (Explained much better over here.)