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

Key path sendability warning should be suppressed #68943

Closed
stephencelis opened this issue Oct 3, 2023 · 9 comments
Closed

Key path sendability warning should be suppressed #68943

stephencelis opened this issue Oct 3, 2023 · 9 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels

Comments

@stephencelis
Copy link
Contributor

Description

The sendability story for key paths is not complete right now, and so users can encounter sendability warnings for them when turning up concurrency warnings in their project. Ideally the warnings a user encounters should highlight problems that can be addressed in their code bases, and not unsolved problems in the language. And yet, key paths emit these warnings nonetheless, and these warnings will persist and cannot be suppressed short of turning down concurrency warnings in the entire project.

Steps to reproduce

We can see the problem by turning up concurrency warnings and compiling the following code:

import SwiftUI

@dynamicMemberLookup struct Box<Value> {
    var value: Value
    subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> T {
        get { value[keyPath: keyPath] }
        set { value[keyPath: keyPath] = newValue }
    }
}

struct Dog {
    var id: Int?
    var name: String = ""
}

struct ContentView: View {
    @State private var state = Box(value: Dog())

    var body: some View {
        TextField("", text: $state.name)  // ⚠️
    }
}

Warning
Cannot form key path that captures non-sendable type 'WritableKeyPath<Dog, String>'

(Thanks @sidepelican for this example.)

Expected behavior

I expect no warning, or for there to be a way to avoid the warning while still having concurrency warnings turned up.

Environment

  • Swift compiler version info
    swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
    Target: arm64-apple-macosx14.0
    
  • Xcode version info
    Xcode 15.0
    Build version 15A240d
    
  • Deployment target: N/A
@stephencelis stephencelis added bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels labels Oct 3, 2023
@stephencelis
Copy link
Contributor Author

@xedin I believe you said that this issue should be resolved in Swift 6 here, but I'm sadly still seeing the problem in the Xcode 16 beta.

@stephencelis
Copy link
Contributor Author

Or scratch that. Updating to Swift 6 language mode fixes. I guess building for <6 still preserves the old warnings?

@xedin
Copy link
Contributor

xedin commented Jun 11, 2024

Yeah, InferSendableFromCaptures feature is enabled starting from swift language mode 6, you can enable it manually via -Xfrontend -enable-upcoming-feature -Xfrontend InferSendableFromCaptures as well.

@stephencelis
Copy link
Contributor Author

@xedin Thanks for confirming! I'll close this now unless you think there's anything outstanding.

@xedin
Copy link
Contributor

xedin commented Jun 11, 2024

Nothing outstanding, thanks!

@stephencelis
Copy link
Contributor Author

stephencelis commented Jun 12, 2024

@xedin Just came across this key path sendability error in Swift 6:

@MainActor
class Foo: NSObject {
  var bar = 0
  var observation: NSKeyValueObservation?

  func f<Value>(_ keyPath: ReferenceWritableKeyPath<Foo, Value>) {
    observation = observe(keyPath) { foo, _ in
      print(foo[keyPath: keyPath])  // 🛑
    }
  }

  func g() {
    f(\.bar)
  }
}

🛑 Capture of 'keyPath' with non-sendable type 'ReferenceWritableKeyPath<Foo, Value>' in a @Sendable closure

Is this an incorrect diagnosis?

I can add & Sendable to the f function, but that breaks the g:

@MainActor
class Foo: NSObject {
  var bar = 0
  var observation: NSKeyValueObservation?

  func f<Value>(_ keyPath: ReferenceWritableKeyPath<Foo, Value> & Sendable) {
    observation = observe(keyPath) { foo, _ in
      print(foo[keyPath: keyPath])  // ✅
    }
  }

  func g() {
    f(\.bar)  // 🛑
  }
}

🛑 Type 'ReferenceWritableKeyPath<Foo, Int>' does not conform to the 'Sendable' protocol

In this case it seems like it should infer the \.bar literal to be sendable, no?

Is there another way to write the above?

@xedin
Copy link
Contributor

xedin commented Jun 12, 2024

This is correct because isolation makes key paths non-Sendable.

@stephencelis
Copy link
Contributor Author

@xedin Ah, so this specifically because of the @MainActor isolation? I suppose if I traffic a @MainActor key path over sendable boundaries but only use it in a @MainActor scope it's my responsibility to use nonisolated(unsafe) wisely?

@xedin
Copy link
Contributor

xedin commented Jun 12, 2024

Indeed, key path is non-Sendable because all of the isolation information is erased effectively which means that it’s not safe to use it anywhere but where it was created.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels
Projects
None yet
Development

No branches or pull requests

2 participants