Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-10246] Support limited implicit pointer conversion when calling C functions. #52646

Closed
atrick opened this issue Mar 31, 2019 · 3 comments
Closed
Assignees
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

Comments

@atrick
Copy link
Member

atrick commented Mar 31, 2019

Previous ID SR-10246
Radar rdar://49456152
Original Reporter @atrick
Type New Feature
Status Closed
Resolution Done
Additional Detail from JIRA
Votes 9
Component/s Compiler
Labels New Feature, LanguageFeatureRequest
Assignee @xedin
Priority Medium

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:

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.

@atrick
Copy link
Member Author

atrick commented Mar 31, 2019

As a follow up, if this works out, we may also consider allowing an UnsafeBufferPointer to be passed to C functions that take a compatible pointer type.

@atrick
Copy link
Member Author

atrick commented Mar 30, 2020

As another follow up, we should emit a warning with fixup when programmers are using `assumingMemoryBound` just to pass a raw pointer into C/ObjC, to just pass the raw pointer or buffer pointer instead.

@atrick
Copy link
Member Author

atrick commented Oct 8, 2021

PR: #37956

SE Proposal: Relax diagnostics for pointer arguments to C functions

https://github.com/apple/swift-evolution/blob/main/proposals/0324-c-lang-pointer-arg-conversion.md

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added swift evolution implemented Flag → feature: A feature that was approved through the Swift evolution process and implemented and removed new feature labels Nov 11, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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
Projects
None yet
Development

No branches or pull requests

2 participants