Skip to content

Commit 7e1ba94

Browse files
authored
[cxx-interop] Add documentation about changes to SWIFT_SHARED_REFERENCE feature in Swift 6.2
* [cxx-interop] Add documentation about calling ctor or static factory of SWIFT_SHARED_REFERENCE types as Swift Initializer * [cxx-interop] Add documentation about inferring SWIFT_SHARED_REFERENCE in c++ inheritance * [cxx-interop] Add SWIFT_RETUNRS_(UN)RETAINED discussions to C++ interop docs * [cxx-interop] Document guarantees and assumptions for non-const C++ shared references passed to a C++ API from Swift
1 parent 23ebe45 commit 7e1ba94

File tree

1 file changed

+113
-5
lines changed

1 file changed

+113
-5
lines changed

documentation/cxx-interop/index.md

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,23 +1239,131 @@ To specify that a C++ type is a shared reference type, use the `SWIFT_SHARED_REF
12391239
class SharedObject : IntrusiveReferenceCounted<SharedObject> {
12401240
public:
12411241
SharedObject(const SharedObject &) = delete; // non-copyable
1242-
1243-
static SharedObject* create();
1242+
SharedObject();
1243+
static SharedObject* _Nonnull create() SWIFT_RETURNS_RETAINED;
12441244
void doSomething();
12451245
} SWIFT_SHARED_REFERENCE(retainSharedObject, releaseSharedObject);
12461246

12471247
void retainSharedObject(SharedObject *);
12481248
void releaseSharedObject(SharedObject *);
12491249
```
12501250
1251+
In the example above, the `SWIFT_RETURNS_RETAINED` annotation specifies that the returned value is passed with `+1` ownership.
1252+
For more details, see [Calling conventions when returning Shared Reference Types from C++ to Swift](#calling-conventions-when-returning-shared-reference-types-from-c-to-swift).
1253+
12511254
Now that `SharedObject` is imported as a reference type in Swift, the programmer will be able to use it in the following manner:
12521255
```swift
1253-
let object = SharedObject.create()
1254-
object.doSomething()
1256+
let object1 = SharedObject.create()
1257+
let object2 = SharedObject() // The C++ constructor is imported as a Swift initializer
1258+
object1.doSomething()
1259+
object2.doSomething()
12551260
// `object` will be released here.
12561261
```
12571262

1258-
### Inheritance and Virtual Member Functions
1263+
#### Constructing objects of Shared Reference Types from Swift
1264+
1265+
As demonstrated in the provided example, starting from Swift 6.2, you can create instances of `SWIFT_SHARED_REFERENCE` types by invoking their initializers.
1266+
Note that the Swift compiler uses the default `new` operator to construct C++ shared reference types.
1267+
1268+
You can also import a user-defined C++ static factory function as a Swift initializer by annotating it with `SWIFT_NAME("init(…)")` annotation macro, ensuring that the number of underscore placeholders matches the number of parameters in the factory function.
1269+
For example:
1270+
1271+
```cpp
1272+
struct SharedObject {
1273+
static SharedObject* make(int id) SWIFT_NAME("init(_:)");
1274+
1275+
void doSomething();
1276+
} SWIFT_SHARED_REFERENCE(retainSharedObject, releaseSharedObject);
1277+
```
1278+
1279+
In this case, Swift will import the static `make` function as a Swift initializer:
1280+
1281+
```swift
1282+
let object = SharedObject(42)
1283+
```
1284+
1285+
Note that if a C++ constructor and a user-annotated static factory (using `SWIFT_NAME`) have identical parameter signatures, Swift favors the static factory when resolving initializer calls.
1286+
This is particularly useful when you want to use a custom allocator or want to disable direct construction entirely and expose only factories.
1287+
1288+
#### Inference of Shared Reference behaviour in Derived Types
1289+
1290+
When a C++ type inherits from a `SWIFT_SHARED_REFERENCE` base type, the Swift compiler automatically infers `SWIFT_SHARED_REFERENCE` annotation for the derived type.
1291+
The derived type also gets imported as a reference type, and uses the same `retain` and `release` functions as its base class.
1292+
This inference works as long as all the annotated base types in the inheritance chain (including multiple or indirect inheritance) have the same `retain` and `release` functions.
1293+
If multiple base types have conflicting `retain` or `release` functions, the derived type is imported as a Swift value type, and the compiler emits a warning.
1294+
1295+
1296+
Note that this inference currently applies only to `SWIFT_SHARED_REFERENCE`.
1297+
It does not apply to types annotated with `SWIFT_IMMORTAL_REFERENCE` or `SWIFT_UNSAFE_REFERENCE`.
1298+
1299+
#### Calling conventions when returning Shared Reference Types from C++ to Swift
1300+
1301+
When C++ functions and methods return `SWIFT_SHARED_REFERENCE` types, it is necessary to specify the ownership of the returned value.
1302+
For this you should use the `SWIFT_RETURNS_RETAINED` and `SWIFT_RETURNS_UNRETAINED` annotations on functions and methods.
1303+
These annotations tell the Swift compiler whether the type is returned as `+1` (retained) or `+0` (unretained).
1304+
1305+
```c++
1306+
// Returns +1 ownership.
1307+
SharedObject* _Nonnull makeOwnedObject() SWIFT_RETURNS_RETAINED;
1308+
1309+
// Returns +0 ownership.
1310+
SharedObject* _Nonnull getUnOwnedObject() SWIFT_RETURNS_UNRETAINED;
1311+
```
1312+
1313+
These annotations are necessary to ensure that appropriate `retain`/`release` operations are inserted at the boundary:
1314+
1315+
```swift
1316+
let owned = makeOwnedObject()
1317+
owned.doSomething()
1318+
// `owned` is already at +1, so no further retain is needed here
1319+
1320+
let unOwned = getUnOwnedObject()
1321+
// Swift inserts a retain operation on `unowned` here to bring it to +1.
1322+
unOwned.doSomething()
1323+
```
1324+
1325+
Note that the Swift compiler will automatically infer the ownership conventons for Swift functions returning `SWIFT_SHARED_REFERENCE` types.
1326+
See [Exposing C++ Shared Reference Types back from Swift](#exposing-c-shared-reference-types-back-from-swift) for calling Swift functions returning `SWIFT_SHARED_REFERENCE` types from C++.
1327+
1328+
#### Calling conventions when passing Shared Reference Types from Swift to C++
1329+
1330+
If a C++ shared reference type is passed as an argument to a C++ API from Swift, the Swift compiler guarantees that the passed value would be alive.
1331+
Swift also retains the ownership of the value.
1332+
In other words, the argument is passed at `+0` and there is no transfer of ownership.
1333+
The C++ function should not assume that it has the ownership of the value and should do necessary retain operations if it is needs to take ownership.
1334+
The C++ function is responsible for ensuring that the value pointed to by the parameter is alive during and at the end of the function call.
1335+
1336+
1337+
```swift
1338+
var obj = SharedObject.create()
1339+
receiveSharedObject(obj) // Swift guarantees that obj is alive and it is passed at +0
1340+
```
1341+
1342+
```c++
1343+
void receiveSharedObject(SharedObject *sobj) {
1344+
...
1345+
// Swift assumes that sobj is a valid, non-null object at the end of this function
1346+
}
1347+
```
1348+
1349+
Note that if the argument is an inout (non-const reference) as shown below:
1350+
1351+
```c++
1352+
void takeSharedObjectAsInout(SharedObject *& x) { ... }
1353+
```
1354+
1355+
which would be imported in Swift as
1356+
1357+
```swift
1358+
func takeSharedObjectAsInout(_ x: inout SharedObject) { ... }
1359+
```
1360+
1361+
The C++ function can overwrite the value of the argument with the new value.
1362+
However, the C++ function is responsible for releasing the old value, and ensuring that the new value is properly retained so that the Swift caller has ownership of the new value when the function returns.
1363+
Adhering to these rules is necessary to safely and correctly pass around `SWIFT_SHARED_REFERENCE` between Swift and C++.
1364+
These rules are also generally recommended conventions to manage shared objects that use reference counting.
1365+
1366+
#### Inheritance and Virtual Member Functions
12591367

12601368
Similar to value types, casting an instance of a derived reference type to a
12611369
base reference type, or vice versa, is not yet supported by Swift.

0 commit comments

Comments
 (0)