Formalizing @cdecl

If you're defining the functions in Swift, then @_cdecl should be fine. If it's crashing, then there's either a bug in the compiler, or a calling convention mismatch between what Swift and LLVM are doing and what the JNI expects. You would use headers and modulemaps to declare external C functions to be imported into Swift.


Just to clarify my use-case, this annotation is great for writing Unity plugins for iOS.

The Unity iOS space is full of plugins written in ObjC. By using @_cdecl, we can write 100% Swift, no ObjC bridges required.

For example: Swift "Hello World" Unity plugin · GitHub


Bumping this thread and wondering if any new decisions have been made on the matter, as well as the viability of continuing to use @_cdecl still without fear of it not being supported in a future release. And if so, how much of an impact would that be (other than refactoring the symbol to @cdecl or something similar)


The implementation is, to the best of my knowledge, functionally complete, so the thing we'd need to advance @_cdecl to an official feature is an evolution proposal. We generally keep existing underscored attribute spellings around for a while for source compatibility, and I don't foresee any massively breaking changes we'd make to an official feature that'd make maintaining the existing behavior for compatibility difficult. The list of issues from Jordan's OP still looks like a good list of topics to hash out in a pitch/review cycle.


Yeah, I’m afraid after starting the topic and getting a lot of good discussion I developed RSI and couldn’t continue spearheading it myself.


I am interested in working on pitching an attribute like @expose that was proposed in this thread. We would like to adopt it for Swift and C++ interop, to control which Swift APIs get exposed to C++ (APIs would have to be explicitly annotated with @expose). I think it could potentially act as a replacement for @_cdecl as well.

So far I have thought about the following behavior for this attribute:

  • @expose - tells the compiler that this declaration is exposed to a foreign language interface (C/C++/Objective-C++, in one unified header). Most likely when applied to something like a struct, it will expose all its public members too. Note that in this case the Swift declaration will still use the underlying Swift ABI.

  • @expose(c++, name) - tells the compiler that this declaration is exposed to a foreign language interface. In the generated C++ interface, this declaration will be renamed with the specified name.

  • @expose(c, name, convention: cdecl) - equivalent to today's @_cdecl(name) essentially.

I am most likely going to work on a formal evolution pitch and proposal for @expose sometime this year, but I'm interested in getting feedback before then as well.


A thought:
Why not expand the existing @convention type attribute to function declarations for this purpose?

To me, that's a different thing—@convention specifies a concrete representation for a specific function value, but function declarations in Swift don't have any particular convention by themselves; we generate entry points for them guided by how they're used. The attribute we're talking about here would be saying that we want to export a symbol with the C calling convention and a particular name based on a Swift function, but that wouldn't mean that that is the only way to call the function.


Coming in quite late, but… I would think the following would be unambiguous?

public func allocate(_ length: Int) -> UnsafeRawPointer { ... }

We also already have @objc[(name)], could we say @c[(name)] or is @c too concise?

What, if anything, would we want to do for C++? Surely we'd want consistency between Objective-C, C, and C++ here?

Doesn’t Swift also have the unique situation of exporting functions with C mangling but Swift calling convention?

@convention could work. I steered away from it cause the existing conventions “c” and “block” don’t match up with the “c” and “objc” and maybe “c++” or “cxx” we’re going to want, but you could argue that that’s because one is the convention for a function value (closure) and the other is the convention for a declaration (not necessarily a function). I think we’d want to put more arguments in there, like @available, but the syntactic space is available for that. (I personally still like @expose best of the options in this thread.)

(Separately, I would love to have @convention(thin) formalized, for a function with the Swift calling convention but no closure context. I don’t know what to name it though. New thread!)

@c is a bit short, and doesn’t follow the Zero-One-Many rule, i.e. if we want to export to more languages in the future we shouldn’t go through all of this again.

I mean, not normally, but there are a few parts of the stdlib that do this. I do think it’s worth supporting: “C mangling” really means “no mangling” or “custom symbol name”, which in turn means “can be looked up in a dylib”. I don’t think that has to be supported in the same feature as exporting the C convention to C, though.

(Formalizing @cdecl - #31 by jrose covers my thoughts on both of these to some extent.)

What if we had both -emit-c-header and -emit-objc-header that took the same code path, except the former would produce an error if you actually had @objc classes/protocols in your module?

(I realise I'm very late to this, FWIW).

I'd rather have something like @export(c). cdecl is a hangover from the 1980s (the Internet suggests it's Microsoft's, but actually I think it comes from Lattice C). Also, if we have @cdecl, Windows folk are going to want @stdcall et al (which I'd probably spell @export(pascal) because that's what it really means).

-emit-header=c,objc,c++,objc++ would let you specify exactly which language(s) you expected your header to work with. You could even go further (potentially) and specify which version, e.g. -emit-header=c11 or -emit-header=c++23.


Everyone reading the first post: there was a lot of discussion the first time around and there are better names in the middle of the thread!


In the context of C++ interoperability we don't intend to let the user control which flavor of the C/C++ language they want to expose in the generated header. We would like to generate a single header file that exposes declarations that are defined in the #if defined(__cplusplus) or the #if defined(__OBJC__) blocks, so that the user will be able to use the interface that's right for their translation unit. They will also always have access to the underlying C declarations if they would like to just use plain C and interoperate with Swift manually without proper bridging support that's provided through C++ functions and classes. Furthermore, we don't intend to allow them to control which C++ standard the header should generate. The header will choose to provide certain declarations or utilize certain C++ clauses (e.g. C++ 20 requires clauses) based on the C++ standard specified by the translation unit that's importing the header.

I would like to use something like -emit-clang-header and alias the old -emit-objc-header flag to it. I actually just recently implemented something like that in the frontend for C++ interoperability (although it aliases in the opposite direction), but it's not yet something that can be adopted by the Driver as it will need to get community approval first.

stdcall and pascal are different—Pascal convention pushes the arguments on the stack in FIFO order so they appear "backwards" in memory, opposite from C, and reserves stack space for the return value before the arguments. stdcall was introduced with Win32 and is basically a C convention but with callee-pop arguments: arguments are pushed in C order, and returns are by-register like in C. Pascal is probably irrelevant today (unless someone wants to keep the Mac OS 9 port going), but stdcall would be interesting for Win32 interop to this day. (Although even there it's contained to Win32—on Win64 the conventions have all been combined into one C-style convention).


Does Delphi use the Pascal calling convention? Embarcadero still sells it as part of RAD Studio, targeting modern platforms including iOS.

@Joe_Groff sadly this is something that I am actively looking into and running into :).

runtime: allow over-aligned types in the runtime by compnerd · Pull Request #42143 · apple/swift (
IRGen: setup foreign calls properly by compnerd · Pull Request #42157 · apple/swift (
CoreFoundation: correct alignment mis-assumption by compnerd · Pull Request #3164 · apple/swift-corelibs-foundation (

were unearthed by reviving the Win32 port for the runtime. I actually am running into problems with calling conventions for foreign functions being dropped on the floor (the current case being closures).

Aha :-) For some reason I had them muddled in my head — probably because Win16 used pascal and then Win32 changed to stdcall so I'd rather assumed that they were the same (I knew they were both callee-pop). I hadn't really appreciated that they'd changed the argument order.

1 Like

Did an official proposal ever come out of this?