1
1
---
2
2
layout : page
3
- title : Safely Mixing Swift and C++
3
+ title : Safely Mixing Swift and C/C ++
4
4
official_url : https://swift.org/documentation/cxx-interop/safe-interop/
5
5
redirect_from :
6
6
- /documentation/cxx-interop/safe-interop.html
@@ -111,9 +111,12 @@ Building the code again will emit a new diagnostic for the `fileName` function a
111
111
missing lifetime annotations. C and C++ functions that return non-escapable types need annotations
112
112
to describe their lifetime contracts via [lifetimebound](https://clang.llvm.org/docs/AttributeReference.html#id8)
113
113
and [lifetime_capture_by](https://clang.llvm.org/docs/AttributeReference.html#lifetime-capture-by) annotations.
114
+ Not all versions of C and C++ support the `[[clang::lifetimebound]]` attribute syntax. Convenience macros for
115
+ lifetime annotations using the GNU style attribute syntax are available in the `lifetimebound.h` header, and we'll
116
+ be using them throughout this document.
114
117
115
118
```c++
116
- StringRef fileName(const std::string &normalizedPath [[clang::lifetimebound]] );
119
+ StringRef fileName(const std::string &normalizedPath __lifetimebound );
117
120
```
118
121
119
122
Adding this annotation to ` fileName ` indicates that the returned ` StringRef ` value has the
@@ -186,7 +189,7 @@ types do not need lifetime annotations.
186
189
187
190
Escapability annotations can also be attached to types via [ API Notes] ( https://clang.llvm.org/docs/APINotes.html ) :
188
191
189
- ```
192
+ ``` yaml
190
193
Tags :
191
194
- Name : NonEscapableType
192
195
SwiftEscapable : false
@@ -253,7 +256,7 @@ Annotating the parameters of a constructor describes the lifetime of the created
253
256
254
257
```c++
255
258
struct SWIFT_NONESCAPABLE View {
256
- View(const int *p [[clang::lifetimebound]] ) : member(p) {}
259
+ View(const int *p __lifetimebound ) : member(p) {}
257
260
...
258
261
};
259
262
```
@@ -268,7 +271,7 @@ the same lifetime as the implicit `this` parameter.
268
271
struct Owner {
269
272
int data;
270
273
271
- View handOutView() const [[clang::lifetimebound]] {
274
+ View handOutView() const __lifetimebound {
272
275
return View(&data);
273
276
}
274
277
};
@@ -284,10 +287,10 @@ In case the attribute is applied to a subset of the parameters, the return
284
287
value might depend on the corresponding arguments:
285
288
286
289
``` c++
287
- View getOneOfTheViews (const Owner &owner1 [[ clang::lifetimebound ]] ,
290
+ View getOneOfTheViews (const Owner &owner1 __ lifetimebound ,
288
291
const Owner &owner2,
289
- View view1 [[ clang::lifetimebound ]] ,
290
- View view2 [[ clang::lifetimebound ]] ) {
292
+ View view1 __ lifetimebound ,
293
+ View view2 __ lifetimebound ) {
291
294
if (coinFlip)
292
295
return View(&owner1.data);
293
296
if (coinFlip)
@@ -313,7 +316,7 @@ Notably, the default constructor of a type is always assumed to create an indepe
313
316
314
317
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.
315
318
316
- ```
319
+ ``` yaml
317
320
Tags :
318
321
- Name : MyClass
319
322
Methods :
@@ -335,7 +338,7 @@ annotation to describe the lifetime of other output values, like output/inout ar
335
338
or globals.
336
339
337
340
` ` ` c++
338
- void copyView (View view1 [[ clang::lifetime_capture_by (view2)]] , View &view2) {
341
+ void copyView(View view1 __lifetime_capture_by (view2), View &view2) {
339
342
view2 = view1;
340
343
}
341
344
` ` `
@@ -351,11 +354,11 @@ private:
351
354
View containedView;
352
355
353
356
public:
354
- void captureView(View v [[clang::lifetime_capture_by (this)]] ) {
357
+ void captureView(View v __lifetime_capture_by (this)) {
355
358
containedView = v;
356
359
}
357
360
358
- void handOut(View &v) const [[clang::lifetime_capture_by (v)]] {
361
+ void handOut(View &v) const __lifetime_capture_by (v) {
359
362
v = containedView;
360
363
}
361
364
};
@@ -366,7 +369,7 @@ considered safe. If an input never escapes from the called function we can use
366
369
the `noescape` annotation :
367
370
368
371
` ` ` c++
369
- void is_palindrome (std::span<int > s [[ clang::noescape ]] );
372
+ void is_palindrome(std::span<int> s __noescape );
370
373
` ` `
371
374
372
375
The lifetime annotations presented in this sections are powerful,
@@ -375,9 +378,9 @@ APIs with such contracts can still be used from Swift,
375
378
but they are imported as unsafe APIs, so that developers are aware
376
379
that they need to take extra care when using these APIs to avoid memory safety violations.
377
380
378
- ## Convenience Overloads for Annotated Spans and Pointers
381
+ # # Safe Overloads for Annotated Spans and Pointers
379
382
380
- C++ APIs often feature parameters that denote a span of memory.
383
+ C and C ++ APIs often feature parameters that denote a span of memory.
381
384
For example, some might have two parameters where one points to a memory buffer
382
385
and the other designates the buffer's size; others might use the
383
386
[`std::span`](https://en.cppreference.com/w/cpp/container/span) type from the
@@ -386,8 +389,12 @@ compiler can bridge those span-like parameters to Swift's
386
389
[`Span`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md)
387
390
and [`MutableSpan`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0467-MutableSpan.md)
388
391
types, and the user with a safe and convenient interface to those imported APIs.
392
+ These interfaces are *in addition to* the interfaces generated without annotations :
393
+ adding additional annotations to C or C++ APIs will not affect Swift code currently
394
+ relying on the plain interface. Adding additional information may alter the signature
395
+ of any existing safe overload however, since only 1 safe overload per imported function is generated.
389
396
390
- ### C++ `std::span` Support
397
+ # ## Safe Overloads for C++ `std::span`
391
398
392
399
APIs taking or returning C++'s `std::span` with sufficient lifetime
393
400
annotations will automatically get safe overloads take or return Swift
@@ -402,11 +409,205 @@ using IntVec = std::vector<int>;
402
409
403
410
| C++ API | Generated Swift overload |
404
411
| --------------------------------------------------------- | -------------------------------------------------------------------- |
405
- | ` void takeSpan(IntSpan x [[clang::noescape]] ); ` | ` func takeSpan(_ x: Span<Int32>) ` |
406
- | ` IntSpan changeSpan(IntSpan x [[clang::lifetimebound]] ); ` | ` @lifetime(x) func changeSpan(_ x: Span<Int32>) -> Span<Int32> ` |
407
- | ` IntSpan changeSpan(IntVec &x [[clang::lifetimebound]] ); ` | ` @lifetime(x) func changeSpan(_ x: borrowing IntVec) -> Span<Int32> ` |
408
- | ` IntSpan Owner::getSpan() [[clang::lifetimebound]] ; ` | ` @lifetime(self) func getSpan() -> Span<Int32> ` |
412
+ | `void takeSpan(IntSpan x __noescape );` | `func takeSpan(_ x : Span<Int32>)` |
413
+ | `IntSpan changeSpan(IntSpan x __lifetimebound );` | `@lifetime(x) func changeSpan(_ x : Span<Int32>) -> Span<Int32>` |
414
+ | `IntSpan changeSpan(IntVec &x __lifetimebound );` | `@lifetime(x) func changeSpan(_ x : borrowing IntVec) -> Span<Int32>` |
415
+ | `IntSpan Owner::getSpan() __lifetimebound ;` | `@lifetime(self) func getSpan() -> Span<Int32>` |
409
416
410
417
These transformations only support top-level `std::span`s. The compiler
411
418
currently does not transform nested `std::span`s. A `std::span<T>` of a non-const
412
419
type `T` is transformed to `MutableSpan<T>` on the Swift side.
420
+
421
+ # ## Safe Overloads for Pointers
422
+
423
+ If an API uses raw pointers rather than `std::span` - perhaps because it's written in C or Objective-C,
424
+ or because it's an older C++ API that doesn't want to break backwards compatibility -
425
+ it can still receive the same interop safety as `std::span`. This added bounds safety doesn't break
426
+ source compatiblity, nor does it affect ABI. Instead it leverages bounds annotations to express the
427
+ pointer bounds in terms of other parameters in the function signature.
428
+
429
+ # ### Annotating Pointers with Bounds Annotations
430
+
431
+ The most common bounds annotation is `__counted_by`. You can apply `__counted_by` to pointer parameters
432
+ and return values to indicate the number of elements that the pointer points to, like this :
433
+
434
+ ` ` ` c
435
+ int calculate_sum(const int * __counted_by(len) values __noescape, int len);
436
+ ` ` `
437
+
438
+ In this example, the function signature on the C side hasn't changed, but the `__counted_by(len)`
439
+ annotation communicates that the `values` and `len` parameters are related - specifically, `values`
440
+ should point to a buffer of at least `len` `int` values. When you annotate a function with a bounds
441
+ annotation like this, the compiler will generate a bounds safe overload : in addition to the imported
442
+ `func calculate_sum(_ values : UnsafePointer<CInt>, _ len: CInt) -> CInt` signature, you will also
443
+ get the an overload with the `func calculate_sum(_ values : Span<CInt>) -> CInt` signature.
444
+ Note that, like for `std::span`, the `__noescape` annotation is necessary to get a safe wrapper using `Span`.
445
+ This signature is not only more ergonomic to work with - since the generated overload does the unpacking
446
+ of base pointer and count for you - but it's also bounds safe. For example, if your API contains parameters
447
+ that share a count, the bounds safe overload will check that they all correspond.
448
+
449
+ ` ` ` c
450
+ void sum_vectors(const int * __counted_by(len) a __noescape,
451
+ const int * __counted_by(len) b __noescape,
452
+ int * __counted_by(len) out __noescape,
453
+ int len);
454
+ ` ` `
455
+ This safe overload will trap if `a.count != b.count || b.count != out.count` :
456
+ ` ` ` swift
457
+ func sum_vectors(_ a: Span<CInt>,
458
+ _ b: Span<CInt>,
459
+ _ out: MutableSpan<CInt>)
460
+ ` ` `
461
+
462
+ If the count of the `Span` parameters is larger than the C function expects and you intentionally want to use
463
+ only a part of it, you can create a slice using `extracting(_:)`.
464
+
465
+ If your API has more complex bounds you can express those with an arithmetic expression, like so :
466
+
467
+ ` ` ` c
468
+ int transpose_matrix(int * __counted_by(columns * rows) values __noescape, int columns, int rows);
469
+ ` ` `
470
+
471
+ In this case the `columns` and `rows` parameters can't be elided :
472
+ ` ` ` swift
473
+ func transpose_matrix(_ values: MutableSpan<CInt>, _ columns: CInt, _ rows: CInt)
474
+ ` ` `
475
+ This is because there's no way to factor out `columns` and `rows` given only `values.count`.
476
+ Instead a bounds check is inserted to verify that `columns * rows == values.count`.
477
+
478
+ When your C/C++ API uses opaque pointers, void pointers or otherwise pointers to unsized types,
479
+ the buffer size can't be described in terms of the number of elements. Instead you can annotate
480
+ these pointers with `__sized_by`. This bounds annotation behaves like `__counted_by`, but takes
481
+ a parameter describing the *number of bytes* in the buffer rather than the *number of elements*.
482
+ Where `__counted_by` maps to `Span<T>`, `__sized_by` will map to
483
+ ` RawSpan` instead.
484
+
485
+ You can access the `__counted_by` and `__sized_by` macro definitions by including the `ptrcheck.h` header.
486
+ For more information about these annotations, see Clang's [bounds safety documentation](https://clang.llvm.org/docs/BoundsSafety.html).
487
+ If the C code base is compiled with `-fbounds-safety`, bounds safety is enforced on the C side as well -
488
+ otherwise it is only enforced at the interop boundary.
489
+
490
+ # ### Lifetime Annotations for Pointers
491
+
492
+ Like with `std::span`, pointers with bounds annotations can have their safe overloads map to
493
+ ` Span` /`MutableSpan` when annotated with the appropriate lifetime annotations from the `lifetimebound.h` header.
494
+ Unlike `std::span`, pointers with bounds annotations also get a bounds safe overload when they lack lifetime annotations :
495
+
496
+ <table>
497
+ <tr><td> C API </td> <td> Generated Swift overload </td></tr>
498
+
499
+ <tr>
500
+ <td markdown=1>
501
+ ` ` ` c
502
+ void
503
+ take_ptr_lifetime(
504
+ const int * __counted_by(len) x __noescape,
505
+ int len);
506
+ ` ` `
507
+ </td>
508
+ <td markdown=1>
509
+ ` ` ` swift
510
+ func take_ptr_lifetime(_ x: Span<Int32>)
511
+ ` ` `
512
+ </td>
513
+ </tr>
514
+
515
+ <tr>
516
+ <td markdown=1>
517
+ ` ` ` c
518
+ const int * __counted_by(len)
519
+ change_ptr_lifetime(
520
+ const int * __counted_by(len) x __lifetimebound,
521
+ int len);
522
+ ` ` `
523
+ </td>
524
+ <td markdown=1>
525
+ ` ` ` swift
526
+ @lifetime(x)
527
+ func change_ptr_lifetime(_ x: Span<Int32>)
528
+ -> Span<Int32>
529
+ ` ` `
530
+ </td>
531
+ </tr>
532
+
533
+ <tr>
534
+ <td markdown=1>
535
+ ` ` ` c
536
+ void
537
+ take_ptr(
538
+ const int * __counted_by(len) x,
539
+ int len);
540
+ ` ` `
541
+ </td>
542
+ <td markdown=1>
543
+ ` ` ` swift
544
+ func take_ptr(_ x: UnsafeBufferPointer<Int32>)
545
+ ` ` `
546
+ </td>
547
+ </tr>
548
+
549
+ <tr>
550
+ <td markdown=1>
551
+ ` ` ` c
552
+ const int * __counted_by(len)
553
+ change_ptr(
554
+ const int * __counted_by(len) x,
555
+ int len);
556
+ ` ` `
557
+ </td>
558
+ <td markdown=1>
559
+ ` ` ` swift
560
+ @lifetime(x)
561
+ func change_ptr(_ x: UnsafeBufferPointer<Int32>)
562
+ -> UnsafeBufferPointer<Int32>
563
+ ` ` `
564
+ </td>
565
+ </tr>
566
+
567
+ </table>
568
+
569
+ The `UnsafeBufferPointer` overloads provide the same bounds safety as their `Span` equvalents
570
+ (NB : ` UnsafeBufferPointer` is not bounds checked on memory access in release builds, but the generated
571
+ ` UnsafeBufferPointer` overloads contain bounds checks in the same cases as the `Span` overloads, *even in release builds*),
572
+ but without lifetime safety. If lifetime information is available the generated safe overload will always
573
+ choose to use `Span` - no `UnsafeBufferPointer` overload will be generated in this case. This means
574
+ that existing callers are not affected by annotating an API with `__counted_by`, but callers using the
575
+ safe overload after adding `__counted_by` *will* be affected if `__noescape` is also added later on, or
576
+ if another parameter is then also annotated with `__counted_by`.
577
+ To prevent source breaking changes, make sure to fully annotate the bounds and lifetimes of an API when
578
+ adding any bounds or lifetime annotations.
579
+
580
+ # ### Bounds Annotations using API Notes
581
+
582
+ In cases where you don't want to modify the imported headers, bounds annotations can be applied using API Notes.
583
+ Given the following header :
584
+ ` ` ` c
585
+ void foo(int *p, int len);
586
+ void *bar(int size);
587
+ ` ` `
588
+ We can provide bounds annotations in our API note file like this :
589
+ ` ` ` yaml
590
+ Functions:
591
+ - Name: foo
592
+ Parameters:
593
+ - Position: 0
594
+ BoundsSafety:
595
+ Kind: counted_by
596
+ BoundedBy: "len"
597
+ - Name: bar
598
+ BoundsSafety:
599
+ Kind: sized_by
600
+ BoundedBy: "size"
601
+ ` ` `
602
+
603
+ # ### Limitations
604
+ Bounds annotations are not supported for nested pointers : only the outermost pointer can be transformed.
605
+
606
+ [`lifetime_capture_by`](https://clang.llvm.org/docs/AttributeReference.html#lifetime-capture-by)
607
+ is currently not taken into account when generating safe overloads.
608
+
609
+ Bounds annotations on global variables or struct fields are ignored : only parameters and return values
610
+ are considered.
611
+
612
+ Bounds annotations, while supported in Objective-C code bases, are not currently supported in Objective-C
613
+ class method signatures.
0 commit comments