Change in how C function signatures are translated in Swift 5?

I decided try out building MongoSwift using the newly released Swift 5.0 development snapshots and encountered some interesting behavior. In particular, the Swift interfaces for a number of C functions are different and there are OpaquePointers expected in many places that expect UnsafeMutablePointers in Swift 4.

MongoSwift uses two C libraries under the hood, libbson and libmongoc. To illustrate the change with a particular example, consider the function bson_new.

Its C signature:

bson_t *
bson_new (void);

Printing out type(of: bson_new) in Swift 4.2 gives me
() -> Optional<UnsafeMutablePointer<_bson_t>>.

But with the 5.0 snapshot, I get:
() -> Optional<OpaquePointer>

I haven't had any luck finding any discussion of these changes in Swift evolution proposals or in the forums. Does anyone have any insight into this?

cc @jrose. There wasn't any intentional change here that I know about. Swift will use OpaquePointer to import a pointer to any type Swift can't import, including incomplete forward-declared C structs. What is the definition of bson_t, and is there any chance it changed its definition between libbson library versions? If it was a struct with a definition that became forward-declared, or it was changed to a typedef to some other kind of type Swift's importer doesn't handle, that could explain the change.


Another (somewhat remote) possibility is that bson_t has an alignment greater than 16 for some reason.


Thanks for the quick reply!

In both cases I am using libbson 1.13, so I don't believe versioning is at play here.

Type definition:

typedef struct {
   uint32_t flags;       /* Internal flags for the bson_t. */
   uint32_t len;         /* Length of BSON data. */
   uint8_t padding[120]; /* Padding for stack allocation. */
} bson_t BSON_ALIGNED_END (128);

Those alignments are probably meant to be bits rather than bytes. :-( No one (to a near approximation) supports a 128-byte alignment.

EDIT: okay, that's a brash and untrue statement. But Swift doesn't, and BSON probably didn't mean to.

1 Like

Well, sizeof(bson_t) does equal 128, so maybe it was intentional. But I have no idea why it would be intentional.

EDIT: You will be shocked to learn that bson_malloc is implemented as simply calling malloc by default and so does not in fact return 128-byte-aligned memory.

1 Like

So I can understand that this puts you in an awkward position, because the library is doing something a little silly and it's leading to poor import results, but you may not feel empowered to actually do anything about it. As a short-term solution, you can hack your headers; as a longer-term solution, you can bring it up with the libbson maintainers; otherwise you might feel a little stuck.

In the long run, Swift should be capable of importing types like this; we just can't allow them to be abstracted over. That's not something we can express right now, so instead we're doing something conservative but correct, and it's not great.


Thanks for the input, all.

I work with the maintainers, so I'll speak with them tomorrow to try to get some insight into what's going on there.

What specifically changed about type importing in Swift 5 that led to this?

We capped alignment at 16 bytes for ABI stability.


Just an assumption but could this be related to

"\x13" e_name decimal128 128-bit decimal floating point

from the BSON specification?!

As far as I know BSON can hold elements of a Decimal128 type.

Very possibly, but that's still 128 bits, not 128 bytes. (And BSON-as-transmission format also doesn't guarantee alignment, by design.)

1 Like

Face palm, yeah I should have read more carefully. Thank you for pointing that out.