Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UniFFI generated Swift code fails to compile "Swift 6" mode - due to mutation of shared state. #2046

Open
Sajjon opened this issue Mar 24, 2024 · 0 comments

Comments

@Sajjon
Copy link
Contributor

Sajjon commented Mar 24, 2024

Whenever we land #2045 (or other PR enabling Sendable types) we should try to ensure that UniFFI generated Swift code compiles (and works...) under "Swift 6" mode, that is with enableExperimentalFeature("StrictConcurrency")

Screenshot 2024-03-24 at 07 25 34

We can already now "emulate Swift 6" mode, by adding:

let swiftSettings: [SwiftSetting] = [
	.enableExperimentalFeature("StrictConcurrency"),
	.unsafeFlags(["-warnings-as-errors"])
]

And then use swiftSettings: swiftSettings in our Swift SPM targets, like so

Doing this shows some issues with how UniFFI sets up Callbacks, specifically with vtable in UniffiCallbackInterface<MY_TRAIT> and handleMap in FfiConverterType<MY_TRAIT>.

The problem is they use static mutable properties, which is verboten in Swift 6.

There are a few ways to solve this, I think perhaps using @TaskLocal is the simplest way forward. See PointFree's episode of ShardState, in this code they have a very similar scenario, a global shared stated inside an enum namespace:

enum SharedLocals { @TaskLocal static var isAsserting = false; }

Here is the documentation about @TaskLocal

We could need to change how uniffiCallbackInit<MY_TRAIT> works

private func uniffiCallbackInit<MY_TRAIT>() {
    uniffi_<MY_PROJECT>_fn_init_callback_vtable_<MY_TRAIT>(&UniffiCallbackInterface<MY_TRAIT>.vtable)
}

to be able to perhaps take to closures instead, using UniffiCallbackInterface<MY_TRAIT>.$vtable.get() as a () -> Value read method, N.B. the dollarsign $ (using the propertywrapper, and not the projected value), probably using withValue on TaskLocal to set. I'm not sure though...

The private class UniffiHandleMap also needs to change, probably it should become an actor instead, potentially a @GlobalActor. But if we are certain about the thread safety of class UniffiHandleMap we can just might get away with @unchecked Sendable marking it, like so: private class UniffiHandleMap<T>: @unchecked Sendable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant