[Pitch 2] Staging in Sendable checking

Hey folks,

A couple weeks ago, @Douglas_Gregor pitched some changes which tried to tackle some of the problems involved in adopting Sendable checking in a module when some of your clients or dependencies may not have updated yet. I had some concerns about how that pitch’s approach might break or hide bugs when modules were eventually updated, as well as with how Objective-C libraries would be able to control the sendability of their types.

Doug agreed that these might be issues, so he asked me to write up a new pitch incorporating solutions for them. The tl;dr is:

  • @predatesConcurrency is still roughly as Doug had it, except that it also covers global actor attributes, and Objective-C declarations are imported as predating concurrency.

  • When you import something that should be Sendable but the module has not been updated to make it Sendable yet, you can work around this by using @Sendable import to silence concurrency diagnostics related to that module. When that module later updates, you’ll either get a warning suggest you remove @Sendable (if all is well) or you’ll get warnings about any concurrency bugs revealed by the module’s update (but they will not be hard errors, so you can keep building your project with the bugs it already had until you find time to fix them).

  • You can mark Objective-C type declarations with __attribute__((swift_attr(“@Sendable”))) to make them Sendable, or __attribute__((swift_attr(“@_nonSendable”))) to give them an unavailable Sendable conformance (like Doug discussed in Improving Sendable yesterday). It should be possible to use Clang pragmas to provide a region-based auditing experience similar to nullability annotations.

I’d love to hear your feedback on this new approach.

Edit: In response to feedback, we'll be going back to -warn-concurrency and using @predatesConcurrency import instead of @Sendable import.

10 Likes

These seem like very sensible additions to me, +1.

1 Like

Enable Swift 6 mode or the -strict-concurrency flag. This causes new errors or warnings to appear when Sendable constraints are violated.

How is the -strict-concurrency flag related to the existing -warn-concurrency flag?

Could the new flag be named -warn-concurrency-strict instead?

Note: In the past, Foundation has provided macros with an NS_ prefix for working more easily with ClangImporter features. However, Foundation is not part of the Swift Evolution process, so adding macros for these new features is out of scope for an Evolution proposal.

Could these macros be provided in a usr/include/swift.h file, in the toolchain or SDKs?

The generated headers (for bridging) also have some useful macros.

Same thing, renamed because we do emit some concurrency warnings even without the “-warn-concurrency” flag. (I’m not married to this change.)

2 Likes

Thank you for working on this, I have a couple of thoughts:

  • I recommend updating the introduction to also talk about global actors, alongside Sendable, because this proposal now covers both.

  • I don't think you need to mention C++ headers; that's exciting but unrelated.

  • I'm not sure how much benefit there is to renaming -warn-concurrency to -strict-concurrency, given that the former is already availability in Swift 5.5, but I don't feel strongly about it.

  • Your full/strict/minimal categorization is spot on, thank you!

  • For @predatesConcurrency, I think we want to spell out how the type of an entity is transformed when mangling and when the entity is referenced from a minimal context.

  • @Sendable import is really nice as a suppression mechanism. I'm wondering if this should be generalized to a @predatesConcurrency import? As in, always treat references to entities in this imported module as "minimal" and perform the transformations described above?

  • All of the ObjC stuff looks very well thought-out to me

    Doug

5 Likes