Yeah, all of the existing attributes for exporting symbols to various FFI are different in very subtle ways, which I believe are the following:
-
func f()
without attributes:-
Symbols emitted into binary: One—a normal Swift function named with the mangling of
f
. -
Decls in generated header: N/A without C++ interop. With C++ interop, an inline trampoline function named
module_name::f
is printed that calls the Swiftf
. -
Swift visibility for which decls are generated in the header:
public
-
Symbols emitted into binary: One—a normal Swift function named with the mangling of
-
@_silgen_name("g") func f()
-
Symbols emitted into binary: One—a normal Swift function, but named
g
instead of the mangling off
. - Decls in generated header: Same as #1.
- Swift visibility for which decls are generated in the header: Same as #1.
-
Symbols emitted into binary: One—a normal Swift function, but named
-
@_cdecl("g") func f()
-
Symbols emitted into binary: Two—a normal Swift function named with the mangling of
f
, and a trampoline C functiong
that callsf
. -
Decls in generated header: An
extern "C"
function declaration for the trampoline is printed. If C++ interop is enabled, a C++ inline trampoline is also printed, but it looks like it invokes the C trampoline instead of the Swift symbol? (generated header in output window) -
Swift visibility for which decls are generated in the header:
public
,internal
-
Symbols emitted into binary: Two—a normal Swift function named with the mangling of
-
@_expose(Cxx, "g") func f()
-
Symbols emitted into binary: One—a normal Swift function named with the mangling of
f
. -
Decls in generated header: N/A without C++ interop. With C++ interop, an inline trampoline function named
module_name::g
is printed that calls the Swiftf
. -
Swift visibility for which decls are generated in the header:
public
-
Symbols emitted into binary: One—a normal Swift function named with the mangling of
-
@objc(g) func f()
(wheref
is a method in aclass
):-
Symbols emitted into binary: Two—a normal Swift function named with the mangling of
f
, and a trampoline Obj-C method namedg
that callsf
. -
Decls in generated header: An Objective-C method decl for the trampoline
g
is printed. -
Swift visibility for which decls are generated in the header:
public
,internal
-
Symbols emitted into binary: Two—a normal Swift function named with the mangling of
So I agree that there's a bit of a spectrum here; @_silgen_name
affects the name of the original symbol, @_cdecl
and @objc
leave the original symbol alone and introduce a new one for external clients, and @_expose(Cxx, ...)
only affects the generated header.
I don't think that's a reason to avoid unifying these concepts altogether (although the ship has already sailed for @objc
, which is both officially supported and very widely used). If we claim that @symbolName
(or whatever it's called) "defines the name that foreign languages interact with a symbol", then I think we could argue that @_cdecl
and @_expose(Cxx, ...)
fit that description even if C++ interop doesn't require it, and I'd feel comfortable saying that @symbolName(swift, ...)
is defining the name that foreign languages (or a foreign process via dladdr
) which aren't using Swift's C++ interop use to interact with a function that still uses a Swift calling convention, since those may be the only times you'd want to rename the Swift symbol. (I suppose maintaining ABI compatibility while renaming an API is another one.)