Formalizing @cdecl

Okay, why @objc @cdecl together? When thinking this through, I considered several options:

  • @cdecl and @objc are both valid on top-level functions, but behave differently: @objc allows using ObjC-only types and @cdecl does not.

    • Upside: better checking of accidental reliance on ObjC interop.
    • Downside: easy to use the wrong one, in theory.
    • Downside: we may eventually want to allow @cdecl on member functions as well, the same way we have import-as-member for C functions, but @objc already means something there.
  • @cdecl and @objc are interchangeable on Apple platforms, i.e. you can use ObjC-only types in @cdecl functions.

    • Downside: accidental reliance on ObjC interop
    • Downside: same thing about member functions
  • @cdecl allows using ObjC-only types on Apple platforms and you can't write @objc on a top-level function at all

    • Downside: accidental reliance on ObjC interop
  • @cdecl does not allow ObjC-only types, and you can't write @objc on a top-level function at all

    • Downside: seems like an artificial limitation - what if you want a top-level C-style function that takes an NSString? Or a CF type?
    • This is my second choice, though, and it's easy to expand it to my original proposal later.
  • @cdecl does not allow ObjC-only types, but @objc @cdecl does

    • Downside: verbose and a little weird-looking
    • Upside: better checking of accidental reliance on ObjC interop
    • Easy to have the compiler help you if you need both but only have one
    • This is my preferred option.

I also thought about increasing interop capabilities so that anything that Swift can expose to Objective-C can be exposed to C as well with some sort of opaque struct pointer type—then there are no more types requiring ObjC interop! Sadly, I came to the conclusion that it was just a dream, because you'd still expose them differently on platforms with/without ObjC interop, and it's unclear whether you'd even be able to tell what to expose from Swift when there's no such thing as "ObjC ancestry". (And you can't expose everything because Swift allows naming conflicts that C does not, thanks to modules and nesting. Additionally, exposing more things than @objc does today would change the behavior of the "infer-@objc-for-me" annotation @objcMembers, which makes it a source-breaking change.)

3 Likes