Skip to content

Commit 722c45b

Browse files
author
Gabor Horvath
committed
Extend the later part of the documentation a bit.
1 parent f6adb06 commit 722c45b

File tree

1 file changed

+105
-27
lines changed
  • documentation/cxx-interop/safe-interop

1 file changed

+105
-27
lines changed

documentation/cxx-interop/safe-interop/index.md

Lines changed: 105 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,7 @@ the `SWIFT_NONESCAPABLE` annotation:
150150

151151
```c++
152152
struct SWIFT_NONESCAPABLE View {
153-
View() : member(nullptr) {}
154153
View(const int *p) : member(p) {}
155-
View(const View&) = default;
156154
private:
157155
const int *member;
158156
};
@@ -162,9 +160,7 @@ Moreover, we can explicitly mark types as `Escapable` using the `SWIFT_ESCAPABLE
162160
annotation:
163161
164162
```c++
165-
struct SWIFT_ESCAPABLE Owner {
166-
...
167-
};
163+
struct SWIFT_ESCAPABLE Owner { ... };
168164
```
169165

170166
The main reason for explicitly annotating a type as `SWIFT_ESCAPABLE` is to make sure
@@ -193,28 +189,63 @@ In this example, `MyList<View>` should be imported as `~Escapable` while `MyList
193189
should be imported as `Escapable`. This can be achieved via conditional escapability
194190
annotations:
195191

196-
```
192+
```c++
197193
template<typename T>
198194
struct SWIFT_ESCAPABLE_IF(T) MyList {
199195
...
200196
};
201197
```
202198

199+
Here, instantiations of `MyList` are imported as `Escapable` when `T` is substituted
200+
with an `Escapable` type.
201+
202+
The `SWIFT_ESCAPABLE_IF` macro can take multiple template parameters:
203+
204+
```c++
205+
template<typename F, typename S>
206+
struct SWIFT_ESCAPABLE_IF(F, S) MyPair {
207+
F first;
208+
S second;
209+
};
210+
```
211+
212+
`MyPair` instantiations are only imported as `Escapable` if both template arguments
213+
are `Escapable`.
214+
215+
`Escapable` types cannot have `~Escapable` fields. The following code snippet will
216+
trigger a compiler error:
217+
218+
```c++
219+
struct SWIFT_NONESCAPABLE View { ... };
220+
struct SWIFT_ESCAPABLE Owner {
221+
View v;
222+
};
223+
```
224+
225+
Escapability annotations will not only help the Swift compiler to import C++ types
226+
safely, it will also help discover missing lifetime annotations as all `~Escapable`
227+
parameters and return values need to be annotated in an API to make its use safe in
228+
Swift.
229+
203230
## Lifetime annotations in detail
204231
205-
The `lifetimebound` attribute can be used to annotate code in various scenarios.
206-
On a constructor, it describes the lifetime of the created object:
232+
The `lifetimebound` attribute on a function parameter or implicit object parameter
233+
indicates that the returned object's lifetime could end when any of the `lifetimebound`
234+
annotated parameters' lifetime ended.
235+
This annotation a constructor describes the lifetime of the created object:
207236
208237
```c++
209238
struct SWIFT_NONESCAPABLE View {
210239
View(const int *p [[clang::lifetimebound]]) : member(p) {}
211-
private:
212-
const int *member;
240+
...
213241
};
214242
```
215243

244+
In this example, the object initialized by the `View` constructor has the same
245+
lifetime as the input argument of the constructor.
246+
216247
In case the attribute is after the method signature, the returned object has
217-
the same lifetime as the `this` object.
248+
the same lifetime as the implicit `this` parameter.
218249

219250
```c++
220251
struct Owner {
@@ -226,27 +257,28 @@ struct Owner {
226257
};
227258
```
228259

229-
In case the attribute is applied to a subset of the formal parameters, the return
260+
Consider a call site like `View v = o.handOutView()`. The `v` object has the same lifetime
261+
as `o`.
262+
263+
In case the attribute is applied to a subset of the parameters, the return
230264
value might depend on the corresponding arguments:
231265

232266
```c++
233-
View getView(const Owner& owner [[clang::lifetimebound]]) {
234-
return View(&owner.data);
235-
}
236-
237-
View getViewFromFirst(const Owner& owner [[clang::lifetimebound]], const Owner& owner2) {
238-
return View(&owner.data);
239-
}
240-
241-
View getViewFromEither(View view1 [[clang::lifetimebound]], View view2 [[clang::lifetimebound]]) {
267+
View getOneOfTheViews(const Owner& owner1 [[clang::lifetimebound]], const Owner& owner2
268+
View view1 [[clang::lifetimebound]], View view2 [[clang::lifetimebound]]) {
269+
if (coinFlip)
270+
return View(&owner1.data);
242271
if (coinFlip)
243272
return view1;
244273
else
245274
return view2;
246275
}
247276
```
248277
249-
Occasionally, a function might return a non-escapable type that in fact has no dependency on any other values.
278+
Here, the returned `View`'s lifetime depends on `owner`, `view1`, and `view2` but it cannot
279+
depend on `owner2`.
280+
281+
Occasionally, a function might return a non-escapable type that has no dependency on any other values.
250282
These types might point to static data or might represent an empty sequence or lack of data.
251283
Such functions need to be annotated with `SWIFT_RETURNS_INDEPENDENT_VALUE`:
252284
@@ -276,17 +308,25 @@ Tags:
276308

277309
Note that APINotes have some limitations around C++, they do not support overloaded functions.
278310

279-
We can use `lifetime_capture_by` annotations for output arguments.
311+
While `lifetimebound` always describes the lifetime dependencies of the return value (or
312+
the constructed object in case of constructors), we can use can use `lifetime_capture_by`
313+
annotation to descibe the lifetime of other output values, like output/inout arguments
314+
or globals.
280315

281316
```c++
282317
void copyView(View view1 [[clang::lifetime_capture_by(view2)]], View &view2) {
283318
view2 = view1;
284319
}
320+
```
285321
286-
struct SWIFT_NONESCAPABLE CaptureView {
287-
CaptureView() : view(nullptr) {}
288-
CaptureView(View p [[clang::lifetimebound]]) : view(p) {}
322+
In this example, `view2` will have get all of the lifetime dependencies of `view1`
323+
after a call to `copyView`. a
324+
325+
We can annotate dependency captured by the implicit `this` object, or
326+
an inout argument capturing `this`:
289327
328+
```c++
329+
struct SWIFT_NONESCAPABLE CaptureView {
290330
void captureView(View v [[clang::lifetime_capture_by(this)]]) {
291331
view = v;
292332
}
@@ -301,11 +341,49 @@ struct SWIFT_NONESCAPABLE CaptureView {
301341

302342
All of the non-escapable inputs need lifetime annotations for a function to be
303343
considered safe. If an input never escapes from the called function we can use
304-
the `noescape` annotation.
344+
the `noescape` annotation:
305345

306346
```c++
307347
void is_palindrome(std::span<int> s [[clang::noescape]]);
308348
```
309349
350+
While the annotations in this section are powerful, they cannot express all of
351+
the lifetime contracts. APIs with inexpressible contracts can be used from Swift,
352+
but they are imported as unsafe APIs and need extra care from the developers
353+
to manually guarantee safety.
354+
310355
## Convenience overloads for annotated spans and pointers
311356
357+
C++ APIs often using standard library types or other constructs like a
358+
pointer and a size to represent buffers that have Swift equivalents like
359+
Swift's `Span` type. These Swift types have additional requirements and
360+
guarantees. When these properties are properly annotated on the C++ side,
361+
the Swift compiler can introduce safe convenience functions to make
362+
interacting with the C++ APIs as effortless as if they were written in Swift.
363+
364+
### C++ span support
365+
366+
APIs taking/returning C++'s `std::span` with sufficient lifetime
367+
annotations will automatically get overloads taking/returning Swift
368+
`Span`.
369+
370+
The following table summarizes the generated convenience overloads:
371+
372+
```c++
373+
using IntSpan = std::span<const int>;
374+
using IntVec = std::vector<int>;
375+
```
376+
377+
| C++ API | Generated Swift overload |
378+
| ------------------------------------------------------- | ------------------------------------------------------------------ |
379+
| void takeSpan(IntSpan x [[clang::noescape]]); | func takeSpan(_ x: Span<Int32>) |
380+
| IntSpan changeSpan(IntSpan x [[clang::lifetimebound]]); | @lifetime(x) func changeSpan(_ x: Span<Int32>) -> Span<Int32> |
381+
| IntSpan changeSpan(IntVec& x [[clang::lifetimebound]]); | @lifetime(x) func changeSpan(_ x: borrowing IntVec) -> Span<Int32> |
382+
| IntSpan Owner::getSpan() [[clang::lifetimebound]]; | @lifetime(self) func getSpan() -> Span<Int32> |
383+
384+
These transformations only support top level `std::span`s, we do not
385+
transform the nested cases. A `std::span` of a non-const type `T` will
386+
be transformed to `MutableSpan<T>` on the Swift wide.
387+
388+
### Annotated pointers
389+

0 commit comments

Comments
 (0)