-
Notifications
You must be signed in to change notification settings - Fork 2.4k
[Pitch] Formalize @cdecl
#2813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
xymus
wants to merge
1
commit into
swiftlang:main
Choose a base branch
from
xymus:cdecl
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
[Pitch] Formalize @cdecl
#2813
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Formalize `@cdecl` | ||
|
||
* Proposal: [SE-NNNN](NNNN-cdecl.md) | ||
* Author: [Alexis Laferrière](https://github.com/xymus) | ||
* Review Manager: TBD | ||
* Status: **Awaiting implementation** | ||
* Implementation: [swiftlang/swift#80744](https://github.com/swiftlang/swift/pull/80744) | ||
* Experimental Feature Flags: `CDecl` for `@cdecl`, and `CImplementation` for `@cdecl @implementation` | ||
* Review: ([pitch](https://forums.swift.org/...)) | ||
|
||
## Introduction | ||
|
||
Implementing a C function in Swift eases integration of Swift and C code. This proposal introduces `@cdecl` to mark functions as callable from C, and enums as representable in C. It provides the same behavior under the `@objc` attribute for Objective-C compatible global functions. To complete the story this proposal adds to the compatibility header a new section for C clients and extends `@implementation` support to global functions. | ||
|
||
> Note: This proposal aims to formalize, clean up and extend the long experimental `@_cdecl`. While experimental this attribute has been widely in use so we will refer to it as needed for clarity in this document. | ||
|
||
## Motivation | ||
|
||
Swift already offers some integration with C, notably it can import declarations from C headers and call C functions. Swift also already offers a wide integration with Objective-C: import headers, call methods, print the compatibility header, and implementing Objective-C classes in Swift with `@implementation`. | ||
|
||
These language features have proven to be useful for integration with Objective-C. Offering a similar language support for C will ease integrating Swift and C, and encourage incremental adoption of Swift in existing C code bases. | ||
|
||
Offering a C compatibility type-checking ensures `@cdecl` functions only reference types representable in C. This type-checking helps cross-platform development as one can define a `@cdecl` while working from an Objective-C compatible environment and still see the restrictions from a C only environment. | ||
|
||
Printing the C representation of `@cdecl` functions in a C header will enable a mixed-source software to easily call the functions from C code. The current generated header is limited to Objective-C and C++ content. Adding a section for C compatible clients with extend its usefulness to this language. | ||
|
||
Extending `@implementation` to support global C functions will provide support to developers through type-checking by ensuring the C declaration matches the corresponding definition in Swift. | ||
|
||
## Proposed solution | ||
|
||
Introduce the `@cdecl` attribute to mark a global function as a C functions implemented in Swift. That function uses the C calling convention and its signature can only reference types representable in C. Its body is implemented in Swift as usual. The signature of that function is printed in the compatibility header using C corresponding types for C clients to import and call it. | ||
|
||
```swift | ||
@cdecl(nameFromC) | ||
func mirror(value: Int) -> Int { return value } | ||
``` | ||
|
||
Allow using the existing `@objc` attribute on a global function to offer the same behavior as `@cdecl` except for allowing for the signature to also reference types representable in Objective-C. The signature of a function marked with `@objc` are printed in the compatibility header using Objective-C corresponding types. | ||
|
||
```swift | ||
@objc(nameFromObjC) | ||
func objectMirror(value: NSObject) -> NSObject { return value } | ||
``` | ||
|
||
> Note: The attribute `@objc` on a global function would be the official version of the current behavior of `@_cdecl`. | ||
|
||
Accept the newly introduced `@cdecl` on enums to identify C compatible enums. A `@cdecl` enum must declare an integer raw type compatible with C. An enum marked with `@cdecl` can be referenced from `@cdecl` or `@objc` functions. It is printed in the compatibility header as a C enum. | ||
|
||
> Note: The attribute `@objc` is already accepted on enums that will be usable from `@objc` global function signatures but not from `@cdecl` functions. | ||
|
||
Extend support for the `@implementation` attribute, introduced in [SE-0436](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0436-objc-implementation.md), to global functions marked with either `@cdecl` or `@objc`. Type-checking ensures the C declaration matches the definition in Swift. Functions marks as `@implementation` are not be printed in the compatibility header. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: "are not be printed" |
||
|
||
## Detailed design | ||
|
||
This proposal affects the language syntax, type-checking and the compatibility header. | ||
|
||
### Syntax | ||
|
||
Required syntax changes are limited to one new attribute and the reuse of two existing attributes. | ||
|
||
Introduce the attribute `@cdecl` expecting a single parameter for the corresponding C name of the function or enum. The compiler should ensure the C name respects the rules of an identifier in C. | ||
|
||
Extend `@objc` to be accepted on global functions, using its parameter to define the C function name instead of the Objective-C symbol. | ||
|
||
Extend `@implementation` to be accepted on global functions. | ||
|
||
### Type-checking of global functions signatures | ||
|
||
Global functions marked with `@cdecl` or `@objc` need type-checking to ensure types used in their signature are representable in the target language. | ||
|
||
Type-checking should notably accept these types in the signature of `@cdecl` global functions: | ||
|
||
- Primitive types defined in the standard library: Int, UInt, Float, Double, UInt8, etc. | ||
- Opaque pointers defined in the standard library.: OpaquePointer, UnsafeRawPointer, and UnsafeMutableRawPointer. | ||
- C primitive types defined in the standard library: CChar, CUnsignedInt, CLong, CLongLong, etc. | ||
- Function references using the C calling convention. | ||
- Enums marked with `@cdecl`. | ||
- Types imported from C headers. | ||
|
||
Type-checking should accept more types in the signature of `@objc` global functions and reject them from `@cdecl` functions: | ||
|
||
- `@objc` classes, enums and protocols. | ||
- Types imported from Objective-C modules. | ||
|
||
For both `@cdecl` and `@objc` global functions, type-checking should reject: | ||
|
||
- Optional non-pointer types. | ||
- Non-`@objc` classes. | ||
- Swift structs. | ||
- Non-`@cdecl` enums. | ||
- Protocol existentials. | ||
|
||
### Type-checking of `@cdecl` enums | ||
|
||
Ensure the raw type is defined to an integer value representable in C. This should be the same check as currently applied to `@objc` enums. | ||
|
||
### `@cdecl @implementation` and `@objc @implementation` | ||
|
||
Ensure that a global function marked with `@cdecl @implementation` or `@objc @implementation` is matched to its corresponding declaration in imported headers. | ||
|
||
### Compatibility header printing | ||
|
||
Print only one compatibility header for all languages as it's currently done for Objective-C and C++, adding a section specific to C. Printing that header can be requested using the existing compiler flags: `-emit-objc-header`, `-emit-objc-header-path` or `-emit-clang-header-path`. | ||
|
||
Print the C block in a way it's parseable by compilers targeting C, Objective-C and C++. To do so ensure that only C types are printed, there's no reliance on non-standard C features, and the syntax is C compatible. | ||
|
||
## Source compatibility | ||
|
||
This proposal preserves all source compatibility. | ||
|
||
Existing adopters of `@_cdecl` can replace it with `@objc` to preserve the same behavior. Alternatively it can be updated to `@cdecl` for the more restrictive C compatibility check, but this will change exactly how the corresponding C function is printed in the compatibility header. | ||
|
||
## ABI compatibility | ||
|
||
Marking a global function with `@cdecl` or `@objc` makes it use the C calling convention. Adding or removing these attributes on a function is ABI breaking. Updating existing `@_cdecl` to `@objc` or `@cdecl` is ABI stable. | ||
|
||
## Implications on adoption | ||
|
||
The changes proposed here are backwards compatible with older runtimes. | ||
|
||
## Future directions | ||
|
||
A valuable addition would be some kind of support for Swift structs. We could consider exposing them to C as opaque pointers or produce some structs with a C layout. | ||
|
||
## Alternatives considered | ||
|
||
### `@cdecl` attribute | ||
|
||
In this proposal we use the `@cdecl` attribute on functions to identify them as C functions implemented in Swift and on enums to identify them as C compatible. This feature is fundamental enough that introducing a new attribute, a familiar one at that, seems appropriate. | ||
|
||
We considered some alternatives: | ||
|
||
- A shorter `@c` would be enough to reference the C language and look appropriate alongside `@objc`. However a one letter attribute would make it had for searches and general discoverability. | ||
|
||
- An official `@expose(c)`, from the experimental `@_expose(Cxx)`, would integrate well with the C++ interop work. It would likely need to be extended to accept an explicit C name. This sounds like a viable path forward however it's not yet official and may associate two independent features too much if the attribute provides a different behavior for C vs C++. | ||
|
||
### `@objc` attribute on global functions | ||
|
||
We use the `@objc` attribute on global functions to identify them as C functions implemented in Swift that are callable from Objective-C. This was more of a natural choice as `@objc` is already widely used for interoperability with Objective-C. | ||
|
||
We considered using instead `@cdecl @objc` to make it more explicit that the behavior is similar to `@cdecl` and extending it to Objective-C is additive. We went against this option as it doesn't add much useful information besides being closer to the compiler implementation. | ||
|
||
### Compatibility header | ||
|
||
We decided to extend the existing compatibility header instead of introducing a new one just for C compatibility. This allows content printed from Objective-C to reference C types printed earlier in the same header. Plus this follows the current behavior of the C++ interop which prints its own block in the same compatibility header. | ||
|
||
Since we use the same compatibility header, we also use the same compiler flags to request it being emitted. We considered adding a C specific flag as the main one `-emit-objc-header` is Objective-C specific. In practice however build systems tend to use the `-path` variant, in that case we have `-emit-clang-header-path` that applies well to the C language. We could add a `-emit-clang-header` flag but the practical use of such a flag would be limited. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wasn't super clear to me, I think that you should lead with the fact that there are two ways Swift can support interop:
and that
@cdecl
/@objc
are for the former and@implementation
for the latter.