It seems to me that @_cdecl actually does two distinct things:
- Constrain the types and calling conventions of a function such that it can be called from C (or other languages with C interop). This overlaps with
@convention(c), but I don’t really know how closely.
- Define the linkage name of a function, which is used for static and dynamic linking and also shows up in backtraces. This overlaps with
@_silgen_name.
I think we should strive for a design where these two concepts are separable. If there is an attribute that does both at once, it should be possible to achieve the same effect by composing two (public) attributes; that is, @_silgen_name should be public (probably under a different name) and ideally @_silgen_name(foo) @convention(c) should be equivalent to @cdecl(foo).
Alternatively, if the combined case is expected to dominate (which seems reasonable), it should be possible to get the effect of only @_silgen_name using something like @cdecl(foo) @convention(swift).
Use case
Renaming symbols is one of those things where language/core library developers might think “nobody else needs to do this”. However, I’m currently using @_silgen_name to add context to backtraces, in a manner inspired by Dispatch:
@inline(never)
@_silgen_name("__LIBRARY_IS_CALLING_A_BLAH_CALLBACK__")
private func invokeBlah<Param>(_ blah: Blah<Param>, param: Param) {
return blah(param)
}
Being able to do this with a condoned attribute and not having to type-erase to an UnsafePointer or something would be nice.
Language orthogonality aside, it’s not entirely obvious that exporting an API to C should default to changing the linkage name. Instead of generating C prototypes for exported Swift functions, an exporter could generate stubs like this:
// Swift
@convention(c) public func glorifyMyInt(_ x: Int64) -> Int64 { ... }
// Generated header
static inline int64_t glorifyMyInt(int64_t x) {
// Strawman: _SC instead of _$ as sigil for cdecl Swift functions
// Could use __asm__ instead, but requiring extensions undermines my point :-)
extern int64_t SCs9MyLibrary07glorifyA3Intys5Int64VADF(int64_t);
return SCs9MyLibrary07glorifyA3Intys5Int64VADF(x);
}
There are at least two good reasons not to do this:
- In cases like fulfilling a C-function-based plug-in API, the linkage name is important. This could be handled with an orthogonal attribute, though.
- For generating bindings for languages other than C, this would be an annoying pain. I think this is the best argument for a public
@cdecl doing both things.
The inline stub approach would perhaps be helpful for @allevato’s hypothetical C++ exporter, though, since the Swift compiler wouldn’t have to care about C++ mangling.
On a very tangential note, if we do get @objc top-level functions and perhaps @cdecl structs, I’d also like to be able to use @objc on top-level constants, at least for strings and numbers (e.g. @objc public let MyLibraryIdentifier = "hello" → either extern NSString *const MyLibraryIdentifier; or static NSString * const MyLibraryIdentifier = @"hello";)