I've noticed that C++ functions with pointer arguments or return values of a SWIFT_SHARED_REFERENCE-bridged type are bridged into Swift with the reference types themselves. For example, this:
My question is, what ownership convention is used for the C++ function? Which pointers are interpreted by Swift as retained (+1) and which are interpreted as borrowed/unowned (+0)? Is there a way to customize this?
Also, something else I've noticed is that Swift assumes the retain/release functions passed to the SWIFT_SHARED_REFERENCE macro are safe to call on null pointers, so it calls retain and release for optional C++ shared reference types regardless of if they're nil or not. I was wondering if that was documented anywhere, since I shot myself in the foot with it and others might too.
We use Swift's conventions for managing foreign reference types. Right now we don't have a good way to customize this. On the Swift side you can still leverage Unmanaged to bridge conventions, though (to return at +0 for example).
Also, something else I've noticed is that Swift assumes the retain/release functions passed to the SWIFT_SHARED_REFERENCE macro are safe to call on null pointers, so it calls retain and releasefor optional C++ shared reference types regardless of if they're nil or not. I was wondering if that was documented anywhere, since I shot myself in the foot with it and others might too.
Swift will respect _Nullable and _Nonnull specifiers, so if you code is annotated with those, I wouldn't expect any problems. By default, pointers will be imported as implicitly unwrapped optionals. Are you sure that you don't have NS_ASSUME_NONNULL_BEGIN though?
Swift will respect _Nullable and _Nonnull specifiers, so if you code is annotated with those, I wouldn't expect any problems. By default, pointers will be imported as implicitly unwrapped optionals. Are you sure that you don't have NS_ASSUME_NONNULL_BEGIN though?
I don't think I have NS_ASSUME_NONNULL_BEGIN, because the only headers I'm importing in my C++ file are <swift/bridging> and <QuartzCore/CoreAnimation.h>. Even after annotating my retain and release functions with _Nonnull, Swift still calls them for null pointers. The retain and release functions are never explicitly called from Swift, since they're managed by ARC. I wonder if Swift assuming they're null-safe is an artifact of objc_retain and objc_release being null-safe.
We use Swift's conventions for managing foreign reference types. Right now we don't have a good way to customize this. On the Swift side you can still leverage Unmanaged to bridge conventions, though (to return at +0 for example).
One of my functions is a constructor that returns a pointer to my foreign reference type. When I use it from Swift and print each retain and release call, it seems that Swift assumes the function is returning a +0 reference even though I want it to be a +1 reference. If I'm not mistaken, Swift normally assumes that functions always return owned values. I'm currently using the hack Unmanaged.passUnretained(constructor()).takeRetainedValue() to fix this, which makes everything work correctly. Also, these retain and release functions are declarations whose implementations are linked later, within an extern "C" block; does that make a difference?
That's interesting, I don't think we have anticipated this behavior. We will document it at the very least so it's clear in the docs. Thanks for pointing it out!
I think IRGen “knows” that optional class values can be retained and released with the same functions as non-optional values. It should be fairly straightforward to make that a little more conditional.
Yeah it sounds like we should treat it as a bug, and fix IRGen to avoid such calls. In the meantime we should still document the existing behavior for 5.9 though
I'm surprised that an empty optional would still retain its payload. Are you sure that all your APIs that return nullable pointers are annotated with _Nullable?