SE-0324: Relax diagnostics for pointer arguments to C functions

The review of SE-0324: Relax diagnostics for pointer arguments to C functions , beings now and runs through September 24, 2021.

Amongst the concluding remarks for SE-307, there was some guidance that the Core Team provided with regards to implicit conversions:

There has been a great deal of interest from the community in generalized implicit conversions, and there continues to be discussions around that topic. The core team does not consider the resolution of that discussion to be a precondition for this proposal. I would appreciate that the review thread remain focused on the proposal as presented, and that parties interested in the generalized implicit conversion continue the discussion at Generalization of Implicit Conversions - Evolution / Discussion - Swift Forums.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager, by email or by direct message in the Swift forums. Please include "SE-0324" somewhere in the subject text if sending review feedback by private correspondence.

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Thank you for helping improve Swift!

Saleem Abdulrasool
Review Manager

12 Likes

Since C pointers aren't going anywhere and seamless support for interoperating with C is a tentpole feature of Swift, the problem being addressed is significant enough to warrant the proposed changes, which stand on their own to improve the user experience when working with C functions.

In limiting changes to supporting implicit conversions allowed by C's pointer aliasing rules for functions imported from C, the proposal is narrowly tailored just as the core team guidance would urge and fits with the feel and direction of Swift. I do like the framing that the changes are about relaxing diagnostics, since the implicit conversions are really given by C--the language in which the relevant APIs have been authored--and this proposal is rather about getting Swift up to speed on what C's rules are.

I haven't really explored how other languages handle interoperating with C's pointer aliasing rules, but I have worked with C APIs in Swift that could have benefitted from this proposed feature. I read through the final version of the proposal rather quickly but studied prior versions in more depth and participated in the preceding discussions.

9 Likes

I'm strongly in favour of this proposal. I think it is an important step in improving the ergonomics of Swift's interop with C, and I think the proposal is nicely scoped to the specific issues it is trying to solve.

3 Likes

Strong +1 from me. I have quite some experience with C and still needed a whole day to find the right solution for this problem - I ended up writing C shims to move the pointer aliasing problem in the C domain after reading all docs about memory bounding rules and the available apis in Swift.
You cannot expect from a Swift developer to know stuff about strict pointer aliasing rules for another Language.

+1, I think this is a great approach to solving this problem.

-Chris

1 Like

Many thanks for a nice solution to a knotty problem...

I'm way out of my depth in raw pointer API's and Swift/C-interop, so I wonder if someone could explain why we choose not be explicit, e.g., UnsafeRawCPointer?

Then instead of

The conversions automatically apply to any function imported by the compiler frontend that handles the C family of languages.

it would be

UnsafeCPointer only applies to functions using C calling conventions and type aliasing

Motivations:

  • Make the source code clear about control over types (and risk of untyped pointers)
  • Some developers might prefer to manage their conversions, minimally in incremental migrations from a bespoke solution to compiler-managed.
  • It might avoid accidental usage when the developer is not aware a library is C-interop. (I can imagine some shops wanting to vet any C API's/libraries.)
  • It might help manage the whole family of C inter-op assumptions that evolve over time.

This just moves the question of "what types convert" to the new UnsafeRawCPointer. We still have to get from that type to a usable Swift type.

We still have to get from that type to a usable Swift type

I don't object to the actual conversions proposed, just when they apply.

Implicit always-on type relaxation for broad inter-op families seems like inviting trouble unnecessarily.

Isn't the developer in the best position to decide when these conversions are "wielded sparingly" for the Core Team's purposes?

Wouldn't making it explicit help the reader? And wouldn't it also be a point of control for language developers, in case conversions varied and needed to be managed over time?

I read the Core Team as permitting implicit conversions, but with some notice/control. The question is what kind of notice or control? A source type or bridge, compiler or linker flag, verbose warning, listing of possibly- or actually- affected API's...?

Perhaps the term "implicit" reserved for those cases without need for notice or control, and these particular conversions are universally applicable and acceptable, so a language-level policy is best.

Perhaps the term "implicit" reserved for those cases without need for notice or control, and these particular conversions are universally applicable and acceptable, so a language-level policy is best.

This is well put. The cases affected by this proposal would not benefit from notice or control. The change does nothing more than reflect the reality of the language that the API was designed for. A language-level policy fits well.

Wouldn't making it explicit help the reader?

In general, I strongly prefer explicit conversions. In the narrow use cases affected by this proposal there would be no benefit. It would decrease readability. And it would largely defeat the purpose of being able to port code that passes pointers between C APIs without figuring out some peculiar Swift construct for handling the pointers. There simply isn't any interesting conversion happening that the reader needs to be aware of.

In practice, this change only affects the use of existing C APIs that were designed anticipating exactly the kind of implicit pointer type conversion proposed here. The goal of this proposal is to allow Swift programmers who are not in a position to make changes to a library to simply use C APIs as they were designed. We intentionally don't want new libraries making use of this conversion. They should instead follow Swift's consistently strict rules for pointer types.

With the proposed solution, we simply say that the C compiler follows C semantics and the Swift compiler follows simpler, more consistent Swift semantics. Having Swift fully adopt C semantics with all its legacy warts causes a bigger problem for Swift in the long term. That's discussed in the proposal, but we can talk more about that if needed.

A generalized solution to this specific interop problem quickly leads to over-engineering and opens up new problems. If we had two pointer types with different semantics, the language's memory model would need to define both semantics and support multiple pointer aliasing modes.

Being able to move back and forth between more and less strict pointers within the same language opens up real safety holes. If you have two valid aliasing C pointers, then convert them both into Swift pointers, you can end up with undefined behavior. The C APIs that require this sort of pointer aliasing never call back into Swift. So the proposed solution is quite safe in practice.

I'm very sympathetic to the broad push-back against adding more pointer types to Swift. UnsafePointer mostly is the "C interop" pointer type. In time, Swift APIs should move away from using pointers. This should accelerate when Swift has alternative, efficient low-level "buffer types".

7 Likes
2 Likes