Skip to content

Commit 3605d1d

Browse files
committed
Focus on lifetime safe variants, rename page title
1 parent 0c318d9 commit 3605d1d

File tree

1 file changed

+33
-25
lines changed
  • documentation/cxx-interop/safe-interop

1 file changed

+33
-25
lines changed

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

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
layout: page
3-
title: Safely Mixing Swift and C++
3+
title: Safely Mixing Swift and C/C++
44
official_url: https://swift.org/documentation/cxx-interop/safe-interop/
55
redirect_from:
66
- /documentation/cxx-interop/safe-interop.html
@@ -429,44 +429,44 @@ The most common bounds attribute is `__counted_by`. You can apply `__counted_by`
429429
and return values to indicate the number of elements that the pointer points to, like this:
430430

431431
```c
432-
int calculate_sum(const int * __counted_by(len) values, int len);
432+
int calculate_sum(const int * __counted_by(len) values [[clang::noescape]], int len);
433433
```
434434

435435
In this example the function signature on the C side hasn't changed, but the `__counted_by(len)`
436436
annotation communicates that the `values` and `len` parameters are related - specifically, `values`
437437
should point to a buffer of at least `len` `int` values. When you annotate a function with a bounds
438438
attribute like this, the compiler will generate a bounds safe overload: in addition to the imported
439439
`func calculate_sum(_ values: UnsafePointer<CInt>, _ len: CInt) -> CInt` signature, you will also
440-
get the an overload with the `func calculate_sum(_ values: UnsafeBufferPointer<CInt>) -> CInt` signature.
440+
get the an overload with the `func calculate_sum(_ values: Span<CInt>) -> CInt` signature.
441441
This signature is not only more ergonomic to work with - since the generated overload does the unpacking
442442
of base pointer and count for you - but it's also bounds safe. For example, if your API contains parameters
443443
that share a count, the bounds safe overload will check that they all correspond.
444444

445445
```c
446-
void sum_vectors(const int * __counted_by(len) a,
447-
const int * __counted_by(len) b,
448-
int * __counted_by(len) out,
446+
void sum_vectors(const int * __counted_by(len) a [[clang::noescape]],
447+
const int * __counted_by(len) b [[clang::noescape]],
448+
int * __counted_by(len) out [[clang::noescape]],
449449
int len);
450450
```
451451
This safe overload will trap if `a.count != b.count || b.count != out.count`:
452452
```swift
453-
func sum_vectors(_ a: UnsafeBufferPointer<CInt>,
454-
_ b: UnsafeBufferPointer<CInt>,
455-
_ out: UnsafeMutableBufferPointer<CInt>)
453+
func sum_vectors(_ a: Span<CInt>,
454+
_ b: Span<CInt>,
455+
_ out: MutableSpan<CInt>)
456456
```
457457

458-
If one of the `UnsafeBufferPointer` parameters is larger than the others and you intentionally want to use
458+
If one of the `Span` parameters is larger than the others and you intentionally want to use
459459
only a part of it, you can create a slice using `extracting(_:)`.
460460

461461
If your API has more complex bounds you can express those with an arithmetic expression, like so:
462462

463463
```c
464-
int transpose_matrix(int * __counted_by(columns * rows) values, int columns, int rows);
464+
int transpose_matrix(int * __counted_by(columns * rows) values [[clang::noescape]], int columns, int rows);
465465
```
466466

467467
In this case the `columns` and `rows` parameters can't be elided:
468468
```swift
469-
func transpose_matrix(_ values: UnsafeMutableBufferPointer<CInt>, _ columns: CInt, _ rows: CInt)
469+
func transpose_matrix(_ values: MutableSpan<CInt>, _ columns: CInt, _ rows: CInt)
470470
```
471471
This is because there's no way to factor out `columns` and `rows` given only `values.count`.
472472
Instead a bounds check is inserted to verify that `columns * rows == values.count`.
@@ -475,30 +475,38 @@ When your C/C++ API uses opaque pointers, void pointers or otherwise pointers to
475475
the buffer size can't be described in terms of the number of elements. Instead you can annotate
476476
these pointers with `__sized_by`. This bounds attribute behaves like `__counted_by`, but takes
477477
a parameter describing the *number of bytes* in the buffer rather than the *number of elements*.
478-
Where `__counted_by` maps to `UnsafeBufferPointer<T>`, `__sized_by` will map to
479-
`UnsafeRawBufferPointer` instead.
478+
Where `__counted_by` maps to `Span<T>`, `__sized_by` will map to
479+
`RawSpan` instead.
480480

481481
You can access the `__counted_by` and `__sized_by` macro definitions by including the `ptrcheck.h` header.
482+
For more information about these annotations, see Clang's [bounds safety documentation](https://clang.llvm.org/docs/BoundsSafety.html).
483+
If the C code base is compiled with `-fbounds-safety` bounds safety is enforced on the C side as well -
484+
otherwise it is only enforced at the interop boundary.
482485

483-
#### Lifetime Safe Overloads for Pointers
486+
#### Lifetime Annotations for Pointers
484487

485-
Like with `std::span`, pointers with bounds annotations can also have their safe overloads map to
486-
`Span`/`MutableSpan`, when annotated with the appropriate lifetime annotations. Not all C versions
488+
Like with `std::span`, pointers with bounds annotations can have their safe overloads map to
489+
`Span`/`MutableSpan` when annotated with the appropriate lifetime annotations. Not all C versions
487490
support the `[[clang::noescape]]` attribute syntax, so convenience macros for lifetime attributes
488-
using the GNU style syntax are available in the `lifetimebound.h` header.
491+
using the GNU style syntax are available in the `lifetimebound.h` header. Unlike `std::span`, pointers
492+
with bounds annotations also get a bounds safe overload when they lack lifetime annotations:
489493

490494
```c
491495
#include <ptrcheck.h>
492496
#include <lifetimebound.h>
493-
````
497+
```
494498

495-
| C API | Generated Swift overload |
496-
| ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
497-
| `void take_ptr(const int * __counted_by(len) x __noescape, int len);` | `func take_ptr(_ x: Span<Int32>)` |
498-
| `const int * __counted_by(len) change_ptr(const int * __counted_by(len) x __lifetimebound, int len);` | `@lifetime(x) func change_ptr(_ x: Span<Int32>) -> Span<Int32>` |
499+
| C API | Generated Swift overload |
500+
| -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
501+
| `void take_ptr_lifetime(const int * __counted_by(len) x __noescape, int len);` | `func take_ptr_lifetime(_ x: Span<Int32>)` |
502+
| `const int * __counted_by(len) change_ptr_lifetime(const int * __counted_by(len) x __lifetimebound, int len);` | `@lifetime(x) func change_ptr_lifetime(_ x: Span<Int32>) -> Span<Int32>` |
503+
| `void take_ptr(const int * __counted_by(len) x, int len);` | `func take_ptr(_ x: UnsafeBufferPointer<Int32>)` |
504+
| `const int * __counted_by(len) change_ptr(const int * __counted_by(len) x, int len);` | `@lifetime(x) func change_ptr(_ x: UnsafeBufferPointer<Int32>) -> UnsafeBufferPointer<Int32>` |
499505

500-
These overloads provide the same bounds safety as their `UnsafeBufferPointer` equvalents, but with
501-
added lifetime safety. If lifetime information is available the generated safe overload will always
506+
The `UnsafeBufferPointer` overloads provide the same bounds safety as their `Span` equvalents
507+
(NB: `UnsafeBufferPointer` is not bounds checked on memory access in release builds, but the generated
508+
`UnsafeBufferPointer` overloads contain bounds checks in the same cases as the `Span` overloads, *even in release builds*),
509+
but without lifetime safety. If lifetime information is available the generated safe overload will always
502510
choose to use `Span` - no `UnsafeBufferPointer` overload will be generated in this case. This means
503511
that existing callers are not affected by annotating an API with `__counted_by`, but callers using the
504512
safe overload after adding `__counted_by` *will* be affected if `__noescape` is also added later on, or

0 commit comments

Comments
 (0)