[swift-evolution] Swift *less* safe than C for imported API that uses opaque struct pointers
Timothy J. Wood
tjw at omnigroup.com
Wed Jun 22 13:20:46 CDT 2016
Currently, APIs that get imported with COpaquePointer make Swift *less* safe than C. Fixing this seems like a breaking change, since it would change the meaning of existing code that uses COpaquePointer.
As a motivating example consider:
import Darwin
var state = copyfile_state_alloc()
print("state = \(state.dynamicType) \(state)")
let acl = acl_init(1)
print("acl = \(acl.dynamicType) \(acl)")
state = acl
print("state = \(state.dynamicType) \(state)")
I’m just using these types since they use opaque structs and are easy to create in this sample, but this pattern is fairly common in other C APIs that try to encapsulate their implementation.
This compiles and builds, silently accepting the bogus code:
DEVELOPER_DIR=/Applications/Xcode-8.0b1.app/Contents/Developer swift c-unsafety.swift
state = Optional<OpaquePointer> Optional(0x00007f9db481b3d0)
acl = Optional<OpaquePointer> Optional(0x00007f9db2944c00)
state = Optional<OpaquePointer> Optional(0x00007f9db2944c00)
The equivalent C version:
copyfile_state_t state = state = copyfile_state_alloc();
acl_t acl = acl_init(1);
state = acl;
produces a warning:
c-unsafety.c:10:8: warning: incompatible pointer types assigning to 'copyfile_state_t' (aka 'struct _copyfile_state *') from 'acl_t' (aka 'struct _acl *')
Would it be feasible to import these sorts of pointers in a safe(r) way by making COpaquePointer generic and faking up a struct tag type? So, these might get imported with something equivalent to:
struct _acl {}
typealias acl_t = COpaquePointer<_acl>
A further problem, though, is that other examples of this use `const` to form a very basic `isa` relationship between two types. For example:
typedef const struct OpaqueJSContext* JSContextRef;
typedef struct OpaqueJSContext* JSGlobalContextRef;
so this approach wouldn’t solve casting between these two C types, but perhaps the name of tagging struct could indicate the `const` (“Const_ OpaqueJSContext”?) or maybe COpaquePointer could be used for `const` and a MutableCOpaquePointer type could be added for the non-const case?
There would also be issues if one Swift module tried to pass one of these to another; the made-up struct tag would need to be in some global namespace to avoid errors passing A.COpaquePointer<OpaqueFoo> to B.COpaquePointer<OpaqueFoo> (though really I don’t consider this to be a big problem -- the surface level API of a Swift module using C libraries that deal with C API should mostly try to hide that internally).
In my “real” cases, I’ve been trying to *immediately* wrap the COpaquePointer in a struct of my own, but this doesn’t handle the ‘isa-like’ relationship between a const/non-const variant of an opaque struct pointer, is much more verbose, and it is easy to mess it up and accidentally cross the streams. And of course, with all the contortions Swift goes to try to be safe, this regression in safety from C is puzzling.
Thanks!
-tim
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160622/79444a8b/attachment.html>
More information about the swift-evolution
mailing list