Uploaded image for project: 'Swift'
  1. Swift
  2. SR-10246

Support limited implicit pointer conversion when calling C functions.

    XMLWordPrintable

    Details

      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:

       

      extern unsigned char *CC_SHA256(const void *data, CC_LONG len, unsigned char *md) 
      

      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:

      import Foundation
      import CommonCrypto
      func digest(_ data: Data) -> Data {
       var sha = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
       sha.withUnsafeMutableBytes { shaBuffer in
         data.withUnsafeBytes { buffer in
           let _ = CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count),
                             shaBuffer.bindMemory(to: UInt8.self).baseAddress)
         }
       }
       return sha
      }
      
      

      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:

      • Allow implicit conversion to Unsafe[Mutable]Pointer<UInt8> or
        Unsafe[Mutable]Pointer<Int8> when the argument type is either
        Unsafe[Mutable]Pointer<T> or Unsafe[Mutable]RawPointer.
      • Allow implicit conversion between signed and unsigned variants of trivial integer types:
        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.

        Attachments

          Activity

            People

            Assignee:
            Unassigned Unassigned
            Reporter:
            atrick Andrew Trick
            Votes:
            9 Vote for this issue
            Watchers:
            14 Start watching this issue

              Dates

              Created:
              Updated: