[SR-10246] Support limited implicit pointer conversion when calling C functions. #52646
Labels
compiler
The Swift compiler in itself
feature
A feature request or implementation
swift evolution implemented
Flag → feature: A feature that was approved through the Swift evolution process and implemented
Additional Detail from JIRA
md5: 73b18e368b031915e28fc03f2497354c
Issue Description:
Swift UnsafePointer is strictly typed. For type safety, Swift must disallow conversion from untyped raw pointers (`Unsafe[Mutable]RawPointer`) back to a typed pointer (`Unsafe[Mutable]Pointer<T>`). This language rule makes C interop safer with respect to pointer aliasing; C generally supports strict aliasing based on pointer type, so enforcing strict pointer types in Swift means that programmers won't run afoul of C language rules when passing pointers from Swift into C functions.
C makes exceptions for some special cases, such as signed vs. unsigned types and treating `char` as effectively untyped for the purpose of aliasing. We don't want to extend those special cases into Swift pointer semantics for multiple reasons.
This creates a problem though, because common C API patterns take advantage of the special-case aliasing rules, making Swift interop particularly painful. In practice, this means that users need to rely on unsafe and confusing Swift "memory binding" APIs, which are very difficult to use correctly.
The most problematic pattern is the `char *` byte buffer API. Here's an example from CommonCrypto:
In Swift, it is recommend to represent byte buffers as UnsafeRaw[Mutable]Pointer, or even better to use the safe Data type provided by Foundation. Currently, users need to manually bind memory at the call site just to generate a pointer with the correct type for this API:
A user should never need to bind memory simply to pass Data's buffer into a C function that accesses the buffer's bytes. There's no general way to hide the memory binding behind a wrapper because doing it correctly requires complete knowledge of all other uses of the buffer. In the above example, `bindMemory` is only safe because Data was responsible for allocating the memory itself, and it will never vend a typed pointer into that memory of some other type.
However, if the compiler knows that the pointer is being passed to a function which must be implemented in C, then it can loosen it's requirements on pointer casting and selectively ignore the "bound" memory type.
Proposal:
When the called function is known to be defined in C:
Unsafe[Mutable]Pointer<Int8> when the argument type is either
Unsafe[Mutable]Pointer<T> or Unsafe[Mutable]RawPointer.
e.g. Unsafe[Mutable]Pointer<Int64> to/from Unsafe[Mutable]Pointer<UInt64>
Note that functions types declared in C/ObjC that may be overriden by
Swift via function pointers or inheritance should not allow any
special implicit conversion. This would be unsafe because it may
expose invalid pointer aliases into Swift code. APIs defined in C/ObjC
that take byte buffers and may be overriden in Swift
should use `void*`/`UnsafeRawPointer` rather than `char *`.
Similarly, Swift APIs that are exposed to C code should use
Unsafe[Mutable]RawPointer types rather than
Unsafe[Mutable]Pointer<UInt8> or Unsafe[Mutable]Pointer<Int8> if there
is any doubt that the C code could pass the same pointer into Swift as
a different type.
The text was updated successfully, but these errors were encountered: