Report: Swift and C++ interoperability project progress in the Swift 5.8 time frame

This post presents the progress that the Swift and C++ interoperability workgroup has made from October 2022 until March 2023 by highlighting the major changes that landed in that time frame. The span of this time frame stretches roughly from the release of Swift 5.7 until the end of March when Swift 5.8 was released.

Note that only some compiler changes mentioned in this post made it into Swift 5.8. The rest of the changes will land in Swift 5.9. Going forward, starting with Swift 5.9, the C++ interoperability progress posts will outline the changes that actually landed in a particular release instead of just covering work that landed across multiple releases on Swift's main branch.

:star2: Evolution

  • The document that outlines the high-level vision for using Swift APIs from C++ has been approved by the Swift language workgroup.
  • The document that outlines the vision for using C++ APIs from Swift received numerous updates following the feedback from the Swift language workgroup. The draft of this document was posted on the forums to solicit additional feedback from the community.

:rocket: Using C++ APIs From Swift

  • Disabled support for instantiating C++ class templates from Swift, as the existing implementation is incompatible with Swift's type system. Class templates specializations that are already instantiated in the imported C++ code can still be used in Swift. For example, you can use a type like VecInt in Swift, if you have a typealias like this one in the C++ header: using VecInt = std::vector<int>;.
  • Added a swift_attr source annotation that allows a C++ class/struct to automatically conform to a specific Swift protocol when it's imported into Swift.
    • This improves support for working with templates in a generic manner in Swift, as it's now possible to annotate a C++ class template as conforming to a Swift protocol. Then, all of the specializations of such C++ class template will automatically conform to that Swift protocol when imported into Swift. Generic Swift code constrained by that same protocol will then work with all of the imported specializations of such C++ class template.
  • Improved the naming of C++ templates in Swift diagnostic messages.
  • Swift code now traps on uncaught exceptions that are not caught in C++ and thus are propagated into the caller's Swift frame. This is supported for Itanium ABI only for now (i.e. non Windows targets).
  • C++ standard library module name has been renamed to CxxStdlib. The std namespace name is still preserved.
  • Enhanced the C++ standard library overlay for easier interoperability between Swift and C++ standard library types:
    • String.init(std.string) is now unlabeled, so now it's possible to convert a C++ string into a Swift string using more familiar String(...) syntax.
    • Added conversions between std::u16string and Swift's String type.
    • Swift types like Array and Set can be initialized directly from C++ collection types like vector thanks to the new CxxConvertibleToCollection protocol.
    • Added CxxSet protocol that provides a contains method for types like std::set.
    • Added CxxDictionary protocol that provides a Swift subscript getter for types like std::map.
    • Added CxxOptional protocol that std::optional conforms to. This makes it possible to convert C++ optional into a Swift optional using the familiar Optional(...) syntax, making it more usable in Swift constructs like if let.
  • Improved performance characteristics of the C++ collection overlay that provides safe access to C++ collections from Swift.
  • Improved support for importing libstdc++.
  • Fixed several issues related to Swift not generating inline C++ code in its compilation unit, and copy/destroy semantics of non-trivial C++ records.

:boomerang: Using Swift APIs from C++

  • Supported public APIs are now exposed by default in the generated header. The @_expose(Cxx) attribute is no longer required.
    • Public APIs that depend on Swift types from external Swift modules (except the standard library) are not exposed by default. An experimental -clang-header-expose-module frontend flag has been added to support exposing such APIs in an experimental manner.
  • Added support for exposing Swift APIs that use C++ types back to Swift. This takes us further in the direction of true bidirectional interoperability.
    • For example, you can now write a Swift function that returns a Swift Array of C++ types and call such function from C++.
  • Exposed Swift generics can now be used in C++17 and C++14 language modes.
    • C++20 is still recommended to get enhanced type checking using C++20 concepts.
  • Actors and their nonisolated members are now exposed to C++.
  • Added support for invoking non-final Swift class members using Swift's virtual dispatch mechanism.
  • Improved ergonomics of Swift's String type in C++:
    • Added a constructor that takes in std::string.
    • Added operator std::string that allows a Swift string to be cast to a C++ string.
  • Improved ergonomics of Swift's Optional type in C++.
  • Moved experimental support for exposing functions that throw behind an experimental Swift feature flag.

:building_construction: Tooling & Ecosystem

  • Clang with index-while-building support now supports indexing Swift APIs from the generated header.
    • With such indexing data, SourceKit-LSP is able to perform actions like jump-to-definition from C++ to Swift.
  • Improved readability of the Swift interface for a C++ module that's generated for IDEs:
    • Inline namespaces are no longer shown (hello std.string; goodbye std.__1.string).
    • Improved the naming of C++ templates in the generated Swift interface.
  • Improved bidirectional debugging support in LLDB.
  • Improved CMake support for mixing Swift and C++. More details are available here.

:train2: Looking ahead

The C++ interoperability workgroup anticipates that the adoption of C++ interoperability will accelerate once Swift 5.9 is released. To support more widespread adoption, we are going to provide source stability for C++ APIs imported into Swift and vice-versa. This means that whenever your Swift code enables C++ interoperability and uses the Swift 5 language mode, it should compile with Swift 5.9 and future versions of the Swift compiler that support the Swift 5 language mode.

Over the next few months the workgroup is going to prioritize:

  • Fixing bugs in existing features.
  • Refining and polishing features to provide best compatibility with future Swift releases.
  • Documenting the support for C++ interoperability on swift.org.
  • Publishing code samples that show how to mix Swift and C++ code in different build environments.

The workgroup is actively testing existing interoperability features and evaluating new Swift language features like move-only types to ensure they're compatible with imported C++ types. We're using the following issue on Github to track "must-fix" issues for Swift 5.9 that we find during testing: ☂️ Tracking issue for C++ interoperability fixes we want for Swift-5.9 · Issue #65808 · apple/swift · GitHub. If you encounter any issues while testing C++ interoperability with Swift 5.9 or Swift development snapshots, please file an issue on Swift's Github.

I would like to thank the members of the C++ interoperability workgroup and the Swift community for their contributions to this project. We have made great strides towards our vision of bidirectional interoperability in the Swift 5.8 time frame, and are excited to see how C++ interoperability is going to be used once Swift 5.9 is released.

22 Likes