diff --git a/documentation/cxx-interop/safe-interop/index.md b/documentation/cxx-interop/safe-interop/index.md new file mode 100644 index 000000000..906a68efd --- /dev/null +++ b/documentation/cxx-interop/safe-interop/index.md @@ -0,0 +1,412 @@ +--- +layout: page +title: Safely Mixing Swift and C++ +official_url: https://swift.org/documentation/cxx-interop/safe-interop/ +redirect_from: +- /documentation/cxx-interop/safe-interop.html +--- + +## Table of Contents +{:.no_toc} + +* TOC +{:toc} + +## Introduction + +This document describes the additional memory safety features that Swift +provides when interoperating with C and C++. It describes the ways to make Swift +aware of certain memory safety aspects of your codebase, as well as how Swift +uses that knowledge to prevent common memory safety issues in your code. + +* * * + +
+C++ interoperability is an actively evolving feature of Swift. +Future releases of Swift might change how Swift and C++ +interoperate, +as the Swift community gathers feedback from real world adoption of C++ +interoperability in mixed Swift and C++ codebases. +Please provide the feedback that you have on the +[Swift Forums](https://forums.swift.org/c/development/c-interoperability/), or +by filing an [issue on GitHub](https://github.com/swiftlang/swift/issues/new/choose). +Future changes to the design or functionality of C++ interoperability will not +break code in existing codebases [by default](#source-stability-guarantees-for-mixed-language-codebases). +
+ +## Overview + +Swift provides memory safety with a combination of language affordances and runtime checking. +However, Swift also deliberately includes some unsafe constructs, such as the `UnsafePointer` and `UnsafeMutablePointer` +types in the standard library. +In some cases, Swift needs additional information that is not present in the C++ type and API declarations +to safely interface with them. This document describes how such code needs to be annotated so that it can be used in strict safety mode. + +### Annotating Foreign Types + +Normally, C++ types imported into Swift are assumed to not impact Swift's memory +safety, and can be used without safety-related restrictions. However, a small +set of C++ APIs e.g. pointers/references and methods returning pointers will be +imported as unsafe (section [Working with C++ references and view types in Swift](https://www.swift.org/documentation/cxx-interop/#working-with-c-references-and-view-types-in-swift) +explains this in more detail.) Under the strict memory safety mode, the compiler will flip the polarity of its safety assumptions. +It will treat all types that are not known to be safe as unsafe, and emit diagnostics for usage of unsafe types without the `unsafe` keyword. +In this section, +we will show how to annotate unsafe C++ types so that they can be accessed safely and correctly from Swift. +Note that the features here are agnostic to whether strict safety mode is on or off. When the strict safety +mode is on, the compiler diagnostics can serve as a guide on how to properly annotate C++ types, and also help ensure +that the code doesn't use unsafe APIs anywhere. When the strict safety mode is turned off, it is still +recommended to adopt these annotation wherever appropriate, especially on C++ types that are potentially +dependent on the lifetimes of other objects. + +Under the strict safety mode, built-in numeric types like `int`, some standard library types like `std::string`, +and aggregate types built from other safe types are considered safe. All other unannotated types +are considered unsafe. + +Let's see what happens when we are trying to use an unannotated type in +strict safety mode. Consider the following C++ type and APIs: + +```c++ +struct StringRef { +public: + ... +private: + const char *ptr; + size_t len; +}; + +std::string normalize(const std::string &path); + +StringRef fileName(const std::string &normalizedPath); +``` + +Let's try to use them from Swift with strict memory safety enabled: + +```swift +func getFileName(_ path: borrowing std.string) -> StringRef { + let normalizedPath = normalize(path) + return fileName(normalizedPath) +} +``` + +Building this code will emit a warning that the `fileName` call is unsafe because +it references the unsafe type `StringRef`. Swift considers `StringRef` unsafe because +it has a pointer member. Pointers in types like `StringRef` can dangle, so we need to take extra +care that the buffer they point to outlives the `StringRef` object they are encapsulated by. + +Swift's [non-escapable types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md) +can have lifetime dependencies. The Swift compiler can track these dependencies +and enforce safety at compile time. To import `StringRef` as a safe type, we +need to mark it as a non-escapable type by annotating the class definition: + +```c++ +struct SWIFT_NONESCAPABLE StringRef { ... }; +``` + +Now the Swift compiler imports `StringRef` as a safe type and no longer +emits a warning about using an unsafe type. + +### Annotating C++ APIs + +Building the code again will emit a new diagnostic for the `fileName` function about +missing lifetime annotations. C and C++ functions that return non-escapable types need annotations +to describe their lifetime contracts via [lifetimebound](https://clang.llvm.org/docs/AttributeReference.html#id8) +and [lifetime_capture_by](https://clang.llvm.org/docs/AttributeReference.html#lifetime-capture-by) annotations. + +```c++ +StringRef fileName(const std::string &normalizedPath [[clang::lifetimebound]]); +``` + +Adding this annotation to `fileName` indicates that the returned `StringRef` value has the +same lifetime as the argument of the `fileName` function. + +Building the project again reveals a lifetime error in the Swift function that calls `fileName`: + +```swift +func getFileName(_ path: borrowing std.string) -> StringRef { + let normalizedPath = normalize(path) + return fileName(normalizedPath) + // error: lifetime-dependent value escapes local scope + // note: depends on `normalizedPath` +} +``` + +The value returned by `fileName` will dangle after the lifetime of +`normalizedPath` ends. We can fix this error by making the caller of +`getFileName` normalize the path prior to calling `getFileName`: + +```swift +// Path needs to be normalized. +func getFileName(_ normalizedPath: borrowing std.string) -> StringRef { + return fileName(normalizedPath) +} +``` + +Alternatively, we could construct and return an `Escapable` value of type such +as `std.string` instead of a dangling `StringRef`: + +```swift +func getFileName(_ path: borrowing std.string) -> std.string { + let normalizedPath = normalize(path) + let ref = fileName(normalizedPath) + return ref.toString() +} +``` + +After annotating the C++ code, the Swift compiler can enforce those lifetime +contracts and help us to write code that is free of memory safety errors. + +## Escapability Annotations in Detail + +Under the strict safety mode, even though compiler warns us when importing +unannotated types, they are still imported as if they are `Escapable` to +maintain backward compatibility. This behavior might change in the future under +a new interoperability version. + +We have already seen that we can import a type as `~Escapable` to Swift by adding +the `SWIFT_NONESCAPABLE` annotation: + +```c++ +struct SWIFT_NONESCAPABLE View { + View(const int *p) : member(p) {} +private: + const int *member; +}; +``` + +Moreover, we can explicitly mark types as `Escapable` using the `SWIFT_ESCAPABLE` +annotation to express that they are not lifetime dependent on any other values: + +```c++ +struct SWIFT_ESCAPABLE Owner { ... }; +``` + +The main reason for explicitly annotating a type as `SWIFT_ESCAPABLE` is to make sure +it is considered a safe type when used from Swift. Functions returning escapable +types do not need lifetime annotations. + +Escapability annotations can also be attached to types via [API Notes](https://clang.llvm.org/docs/APINotes.html): + +``` +Tags: +- Name: NonEscapableType + SwiftEscapable: false +- Name: EscapableType + SwiftEscapable: true +``` + +In case of template instantiations the escapability of a type can depend on the +template arguments: + +```c++ +MyList f(); +MyList g(); +``` + +In this example, `MyList` should be imported as `~Escapable` while `MyList` +should be imported as `Escapable`. This can be achieved via conditional escapability +annotations: + +```c++ +template +struct SWIFT_ESCAPABLE_IF(T) MyList { + ... +}; +``` + +Here, instantiations of `MyList` are imported as `Escapable` when `T` is substituted +with an `Escapable` type. + +The `SWIFT_ESCAPABLE_IF` macro can take multiple template parameters: + +```c++ +template +struct SWIFT_ESCAPABLE_IF(F, S) MyPair { + F first; + S second; +}; +``` + +`MyPair` instantiations are only imported as `Escapable` if both template arguments +are `Escapable`. + +`Escapable` types cannot have `~Escapable` fields. The following code snippet will +trigger a compiler error: + +```c++ +struct SWIFT_NONESCAPABLE View { ... }; +struct SWIFT_ESCAPABLE Owner { + View v; +}; +``` + +Escapability annotations will not only help the Swift compiler to import C++ types +safely, it will also help discover missing lifetime annotations as all `~Escapable` +parameters and return values need to be annotated in an API to make its use safe in +Swift. + +## Lifetime Annotations in Detail + +The `lifetimebound` attribute on a function parameter or implicit object parameter +indicates that the returned object's lifetime could end when any of the `lifetimebound` +annotated parameters' lifetime ended. +Annotating the parameters of a constructor describes the lifetime of the created object: + +```c++ +struct SWIFT_NONESCAPABLE View { + View(const int *p [[clang::lifetimebound]]) : member(p) {} + ... +}; +``` + +In this example, the object initialized by the `View` constructor has the same +lifetime as the argument `p` of the constructor. + +When the attribute is written after the method signature, the returned object has +the same lifetime as the implicit `this` parameter. + +```c++ +struct Owner { + int data; + + View handOutView() const [[clang::lifetimebound]] { + return View(&data); + } +}; +``` + +For a call site like: +```c++ +View v = o.handOutView() +``` +the `v` object has the same lifetime as `o`. + +In case the attribute is applied to a subset of the parameters, the return +value might depend on the corresponding arguments: + +```c++ +View getOneOfTheViews(const Owner &owner1 [[clang::lifetimebound]], + const Owner &owner2, + View view1 [[clang::lifetimebound]], + View view2 [[clang::lifetimebound]]) { + if (coinFlip) + return View(&owner1.data); + if (coinFlip) + return view1; + else + return view2; +} +``` + +Here, the returned `View`'s lifetime depends on `owner`, `view1`, and `view2` but not on `owner2`. + +Occasionally, a function might return a non-escapable type that has no dependency on any other values. +These types might point to static data or might represent an empty sequence or lack of data. +Such functions need to be annotated with `SWIFT_RETURNS_INDEPENDENT_VALUE`: + +```c++ +View returnsEmpty() SWIFT_RETURNS_INDEPENDENT_VALUE { + return View(); +} +``` + +Notably, the default constructor of a type is always assumed to create an independent value. + +We can also attach `lifetimebound` annotations to C and C++ APIs using [API Notes](https://clang.llvm.org/docs/APINotes.html). The `-1` index represents the `this` position. + +``` +Tags: +- Name: MyClass + Methods: + - Name: annotateThis + Parameters: + - Position: -1 + Lifetimebound: true + - Name: methodToAnnotate + Parameters: + - Position: 0 + Lifetimebound: true +``` + +Note that API Notes have some limitations around C++, they do not support overloaded functions. + +While `lifetimebound` always describes the lifetime dependencies of the return value (or +the constructed object in case of constructors), we can use can use the [`lifetime_capture_by`](https://clang.llvm.org/docs/AttributeReference.html#lifetime-capture-by) +annotation to describe the lifetime of other output values, like output/inout arguments +or globals. + +```c++ +void copyView(View view1 [[clang::lifetime_capture_by(view2)]], View &view2) { + view2 = view1; +} +``` + +Here, `view2` inherits the lifetime dependencies of `view1`, so callers of `copyView` must ensure that whatever they pass as `view2` does not outlive `view1`. + +We can use this annotation to specify that a parameter's lifetime dependencies are captured by the implicit `this` object, or +conversely, that an inout argument captures the lifetime dependencies of `this`: + +```c++ +struct SWIFT_NONESCAPABLE CaptureView { +private: + View containedView; + +public: + void captureView(View v [[clang::lifetime_capture_by(this)]]) { + containedView = v; + } + + void handOut(View &v) const [[clang::lifetime_capture_by(v)]] { + v = containedView; + } +}; +``` + +All of the non-escapable arguments need lifetime annotations for a function to be +considered safe. If an input never escapes from the called function we can use +the `noescape` annotation: + +```c++ +void is_palindrome(std::span s [[clang::noescape]]); +``` + +The lifetime annotations presented in this sections are powerful, +but there are still lifetime contracts that they cannot express. +APIs with such contracts can still be used from Swift, +but they are imported as unsafe APIs, so that developers are aware +that they need to take extra care when using these APIs to avoid memory safety violations. + +## Convenience Overloads for Annotated Spans and Pointers + +C++ APIs often feature parameters that denote a span of memory. +For example, some might have two parameters where one points to a memory buffer +and the other designates the buffer's size; others might use the +[`std::span`](https://en.cppreference.com/w/cpp/container/span) type from the +C++ standard library. When such APIs are appropriately annotated, the Swift +compiler can bridge those span-like parameters to Swift's +[`Span`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md) +and [`MutableSpan`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0467-MutableSpan.md) +types, and the user with a safe and convenient interface to those imported APIs. + +### C++ `std::span` Support + +APIs taking or returning C++'s `std::span` with sufficient lifetime +annotations will automatically get safe overloads take or return Swift +`Span` and `MutableSpan` types. + +The following table summarizes the generated convenience overloads: + +```c++ +using IntSpan = std::span; +using IntVec = std::vector; +``` + +| C++ API | Generated Swift overload | +| --------------------------------------------------------- | -------------------------------------------------------------------- | +| `void takeSpan(IntSpan x [[clang::noescape]]);` | `func takeSpan(_ x: Span)` | +| `IntSpan changeSpan(IntSpan x [[clang::lifetimebound]]);` | `@lifetime(x) func changeSpan(_ x: Span) -> Span` | +| `IntSpan changeSpan(IntVec &x [[clang::lifetimebound]]);` | `@lifetime(x) func changeSpan(_ x: borrowing IntVec) -> Span` | +| `IntSpan Owner::getSpan() [[clang::lifetimebound]];` | `@lifetime(self) func getSpan() -> Span` | + +These transformations only support top-level `std::span`s. The compiler +currently does not transform nested `std::span`s. A `std::span` of a non-const +type `T` is transformed to `MutableSpan` on the Swift side.