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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to