There was concern that the restriction is too soft and too advisory, since it is not transitively enforced. Async functions can freely call unannotated non-async functions, which in turn can freely call nonasync functions on behalf of the async caller. Despite not being a bulletproof transitive property, the Core Team still believes that the guidance provided by non-transitive enforcement is valuable. Unannotated non-async wrappers can help shape the usage of the unavailable-from-async raw APIs in ways that make them safer to use from async contexts; for instance, if a lock/unlock API is made unavailable from async, because it is generally not a good idea to take a lock in a task when the unlock may happen while the task is running on a different thread, then the library can provide a scoped withLock API that isn't made unavailable from async. Although the wrapper is calling functions that were individually considered unsafe to use from async, since the wrapper would itself be non-async, it avoids the problem of potentially switching threads mid-execution, thereby making the entire operation safer to use from async contexts. Even if it isn't a strict, transitive prohibition on accessing certain operations from async contexts, this proposal provides a tool for guiding users toward the correct code patterns to use instead.
There were concerns about using @available syntax to express unavailable-from-async. This direction actually arose as part of the pitch discussion, and the Core Team believes it provides important functionality. @available already supports a message argument that allows library authors to customize the diagnostic that the compiler presents if developers try to use the unavailable API. We foresee that as being particularly useful with unavailable-from-async, since it will be important to steer developers toward the appropriate async alternatives.
Thanks to everyone who participated in the review!
I still have issues with this proposal that I don't feel were addressed, either by the author during the review or by the core team's rationale.
Sure, I don't think anybody disagrees with the principle. The argument is that just wrapping the unavailable-from-async function in an unannotated function is not the right way to go about it. As written, you could easily expose async code to these APIs by accident - indeed, that will be the default.
IMO, if you are wrapping an unavailable-from-async function in a way that makes it safe for async code, that should be an explicit thing. It shouldn't be possible to do by accident.
This still feels like a case of using something that already exists, even if it isn't a good fit, just to save implementation effort. As I mentioned during the review, we are planning for whole categories of attributes which would like similar availability constraints, and this solution does not scale.
I would have appreciated some feedback from the proposal author on these points during the review. It was a quiet review; there were only a handful of comment to respond to.
A transitive restriction would definitely be more powerful for asserting correctness, but would also be much more disruptive to the existing ecosystem, and in our judgment, the Core Team didn't think the cost/benefit tradeoff of a transitive restriction made sense for this functionality, especially when there is already a large transition underway for Sendable restrictions coming in Swift 6.
I'm sorry you didn't get more direct engagement during the review. The Core Team did discuss this point, though, and we decided to go with the proposed availability-based syntax for the reasons mentioned—particulary because public discussion in the pitch had already steered the originally-proposed design toward availability. It is a reasonable concern whether this is a stretch of the availability constraint, but the availability syntax already has several uses not directly related to OS availability. You are right that there are looming scalability issues for generalizing versioning across all attributes it's interesting for. (My own thoughts on that are that our existing availability scheme is already struggling to scale for clients such as the standard library that rely heavily on ABI stability and versioning, and we should probably consider an alternative approach to attributes in the implementation source code for managing interface versioning after a certain point.)
No, the noasync spelling was kept. The non negation-preface proposal wasn't accepted and nonasync sounds like something that isn't async instead of something that can't be used from an async context. Technically that's true, you can only annotate synchronous functions as being noasync, but that isn't the message it's trying to convey. unavailableFromAsync probably would have been the clearest spelling, but noasync was requested in the discussions.