You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/CppInteroperability/SwiftToCppInteroperabilityVisionDocument.md
+7-7Lines changed: 7 additions & 7 deletions
Original file line number
Diff line number
Diff line change
@@ -28,21 +28,21 @@ When Swift APIs get exposed to C++, the Swift compiler and the language specific
28
28
29
29
Safety is a top priority for the Swift programming language. Swift code expects its caller to adhere to Swift’s type rules and Swift’s memory model, regardless of whether it’s called from Swift or C++. Thus, the C++ code that calls Swift should properly enforce Swift’s expected language contracts. Such enforcement is needed to minimize the risk of Swift behaving in an unexpected manner at run-time when it’s being called from C++, as such behavior could lead to buggy program behavior or crashes that a Swift programmer would not expect to see in their Swift code. This kind of enforcement does not prevent all possible issues though. For instance, a bug in user’s C++ code could accidentally overwrite a Swift object stored on the heap, which could cause unexpected behavior or crashes in Swift code. To catch bugs like this one, the user should use other program analysis tools, such as Address Sanitizer.
30
30
31
-
Swift expects that values of correct types are passed to Swift APIs. For instance, for calls to generic functions, Swift expects the caller to pass a value of type that conforms to all of the required generic requirements. Type safety should be enforced from C++ as well. The C++ compiler should verify that correct types are passed to the Swift APIs as they’re invoked from C++. A program that tries to pass an incorrect Swift type into a Swift function should not compile. Select uses of specific Swift types might be incompatible with static type validation. For example, verification of generic requirements for Swift opaque type value returned from a Swift function in C++ requires run-time validation. The program should report a fatal error when such run-time type validation fails, to ensure that the type invariants that Swift expects are not violated. The reported fatal error should clearly indicate why type validation failed and point to the location in users code which caused the run-time check.
31
+
Swift expects that values of correct types are passed to Swift APIs. For instance, for calls to generic functions, Swift expects the caller to pass a value of type that conforms to all of the required generic requirements. Type safety should be enforced from C++ as well. The C++ compiler should verify that correct types are passed to the Swift APIs as they’re invoked from C++. A program that tries to pass an incorrect Swift type into a Swift function should not compile. Select uses of specific Swift types might be incompatible with static type validation. For example, verification of generic requirements for a Swift opaque type value returned from a Swift function in C++ requires run-time validation. The program should report a fatal error when such run-time type validation fails, to ensure that the type invariants that Swift expects are not violated. The reported fatal error should clearly indicate why type validation failed and point to the location in user's code which caused the run-time check.
32
32
33
33
Memory safety is paramount for Swift code. Swift automatically manages the lifetime of value and reference types in Swift. These principles should translate to C++ as well. The lifetime of Swift values that are created or passed around in C++ should be managed automatically. For instance, the generated C++ code should increment the retain count of a Swift class instance when a Swift class type value is copied in C++, and it should decrement the retain count of a Swift class instance when a Swift class type value is destroyed in C++. The default convention for using Swift types in C++ should discourage dangling references and any other patterns that could lead to an invalid use of a Swift value.
34
34
35
-
Swift’s memory safety model also requires exclusive access to a value in order modify that value. For instance, the same value can not be passed to two inout parameters in the same function call. Swift enforces exclusivity using both compile-time and run-time checks. The generated run-time checks trap when an exclusivity violation is detected at runtime. Calls into Swift APIs from C++ should verify exclusivity for Swift values as well.
35
+
Swift’s memory safety model also requires exclusive access to a value in order to modify that value. For instance, the same value can not be passed to two `inout` parameters in the same function call. Swift enforces exclusivity using both compile-time and run-time checks. The generated run-time checks trap when an exclusivity violation is detected at runtime. Calls into Swift APIs from C++ should verify exclusivity for Swift values as well.
36
36
37
37
### Performance
38
38
39
-
Swift to C++ bridging should be as efficient as possible. The Swift compiler should avoid unnecessary overhead for calling into Swift functions from C++, or using Swift types from C++. Furthermore, the bridging code should not convert Swift types into their C++ counterparts automatically as that can add a lot of overhead. For instance, a Swift function returning a String should still return a Swift String in C++, and not a `std::string`.
39
+
Swift to C++ bridging should be as efficient as possible. The Swift compiler should avoid unnecessary overhead for calling into Swift functions from C++, or using Swift types from C++. Furthermore, the bridging code should not convert Swift types into their C++ counterparts automatically as that can add a lot of overhead. For instance, a Swift function returning a `String` should still return a Swift `String` in C++, and not a `std::string`.
40
40
41
41
Some Swift features require additional overhead to be used in C++. Resilient value types are a good example of this; C++ expects types to have a statically-known layout, but Swift's resilient value types do not satisfy this, and so the generated C++ types representing those types may need to dynamically allocate memory internally. In cases like these, the C++ interface should at least strive to minimize the dynamic overhead, for example by avoiding allocation for sufficiently small types.
42
42
43
43
### Achieving Safety With Performance In Mind
44
44
45
-
Certain aspects of Swift’s memory model impose certain restrictions that create tension between the goal of achieving safety and the goal of avoiding unnecessary overhead for calling into Swift from C++. Checking for exclusivity violations is a good example of such tension. The C++ compiler does not have a notion of exclusivity it can verify, so it is difficult to prove that a value is accessed exclusively in the C++ code that calls into Swift. This means that the C++ code that calls into Swift APIs will most likely require more run-time checks to validate exclusivity than similar Swift code that calls the same Swift APIs.
45
+
Certain aspects of Swift’s memory model impose certain restrictions that create tension between the goal of achieving safety and the goal of avoiding unnecessary overhead for calling into Swift from C++. Checking for exclusivity violations is a good example of this. The C++ compiler does not have a notion of exclusivity it can verify, so it is difficult to prove that a value is accessed exclusively in the C++ code that calls into Swift. This means that the C++ code that calls into Swift APIs will most likely require more run-time checks to validate exclusivity than similar Swift code that calls the same Swift APIs.
46
46
47
47
The adherence to Swift’s type and memory safety rules should be prioritized ahead of performance when C++ calls into Swift, even if this means more run-time checks are required. This is needed to avoid unexpected crashes or other unexpected behavior in users Swift code. For users seeking maximum performance, Swift provides additional compiler flags that avoid certain run-time checks. Those flags should be taken into account when the C++ header is generated. An example of such flag is `-enforce-exclusivity`. When `-enforce-exclusivity=none` is passed to Swift, the Swift compiler does not emit any run-time checks that check for exclusivity violations. A flag like this should also affect the generated C++ header, and the Swift compiler should not emit any run-time checks for exclusivity in the generated C++ header when this flag is used.
48
48
@@ -68,17 +68,17 @@ Swift API authors should not change the way they write Swift code and design Swi
68
68
69
69
### Objective-C Support
70
70
71
-
The existing Swift to Objective-C bridging layer should still be supported even when C++ bindings are generated in the generated header. Furthermore, the generated C++ bindings should use appropriate Objective-C++ types or constructs in generated C++ declarations where it makes sense to do so. For instance, a Swift function that returns an Objective-C class instance should be usable from an Objective-C++ compilation unit.
71
+
The existing Swift to Objective-C bridging layer should still be supported even when C++ bindings are generated in the generated header. Furthermore, the generated C++ bindings should use appropriate Objective-C++ types or constructs in the generated C++ declarations where it makes sense to do so. For instance, a Swift function that returns an Objective-C class instance should be usable from an Objective-C++ compilation unit.
72
72
73
73
## The Approach
74
74
75
75
The Swift compiler exposes Swift APIs to C++ by generating a header file that contains C++ declarations that wrap around the underlying calls into Swift code and provide a suitable representation for Swift types. Typically, a single header file is generated for one Swift module.
76
76
77
77
The generated header file depends on specific LLVM and Clang compiler features for Swift calling convention support, and thus it can only be compiled by Clang. The header does not depend on any Clang-specific C++ language extensions. The header might depend on other Clang-only features that improve developer experience for the C++ users, like specific attributes that improve diagnostics.
78
78
79
-
The generated header file uses advanced C++ features that require a recent C++ language standard. C++20 is the recommend language standard to use for Swift APIs, however, the generated header should also be compatible with C++17 and C++14.
79
+
The generated header file uses advanced C++ features that require a recent C++ language standard. C++20 is the recommended language standard to use for Swift APIs, however, the generated header should also be compatible with C++17 and C++14.
80
80
81
-
The generated header file also contains the C and Objective-C declarations that Swift exposes to C and Objective-C on platforms that support C or Objective-C interoperability. Thus a single header file can be used by codebases that mix C, Objective-C and C++.
81
+
The generated header file also contains the C and the Objective-C declarations that Swift exposes to C and Objective-C on platforms that support C or Objective-C interoperability. Thus a single header file can be used by codebases that mix C, Objective-C and C++.
82
82
83
83
The generated header file is a temporary build artifact. On platforms with ABI stability, the generated C++ code for an ABI stable Swift interface is not tied to the Swift compiler that compiled the Swift code for that Swift module, as ABI stability is respected by the C++ code in the header. In all other cases the generated C++ code in the header is assumed to be tied to the Swift compiler that compiled the Swift module, and thus the header should be regenerated when the compiler changes.
0 commit comments