-
Notifications
You must be signed in to change notification settings - Fork 10.5k
🍒 [6.2] [cxx-interop] Introduce type-level annotation to specify default ownership convention for C++ foreign reference return values #81266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fahadnayyar
wants to merge
4
commits into
swiftlang:release/6.2
Choose a base branch
from
fahadnayyar:fahadnayyar/release/6.2/cxx-frt-default-returns-unretained-final
base: release/6.2
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…ership convention for C++ foreign reference return values (swiftlang#81093) In Swift 6.1, we introduced `SWIFT_RETURNS_RETAINED` and `SWIFT_RETURNS_UNRETAINED` annotations for C++ APIs to explicitly specify the ownership convention of `SWIFT_SHARED_REFERENCE` type return values. Currently the Swift compiler emits warnings for unannotated C++ APIs returning `SWIFT_SHARED_REFERENCE` types. We've received some feedback that people are finding these warnings useful to get a reminder to annotate their APIs. While this improves correctness , it also imposes a high annotation burden on adopters — especially in large C++ codebases. This patch addresses that burden by introducing two new type-level annotations: - `SWIFT_RETURNED_AS_RETAINED_BY_DEFAULT` - `SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT` These annotations allow developers to specify a default ownership convention for all C++ APIs returning a given `SWIFT_SHARED_REFERENCE`-annotated type, unless explicitly overridden at the API by using `SWIFT_RETURNS_RETAINED` or `SWIFT_RETURNS_UNRETAINED`. If a C++ class inherits from a base class annotated with `SWIFT_RETURNED_AS_RETAINED_BY_DEFAULT` or `SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT`, the derived class automatically inherits the default ownership convention unless it is explicitly overridden. This strikes a balance between safety/correctness and usability: - It avoids the need to annotate every API individually. - It retains the ability to opt out of the default at the API level when needed. - To verify correctness, the user can just remove the `SWIFT_RETURNED_AS_(UN)RETAINED_BY_DEFAULT` annotation from that type and they will start seeing the warnings on all the unannotated C++ APIs returning that `SWIFT_SHARED_REFERENCE` type. They can add `SWIFT_RETURNS_(UN)RETAINED` annotation at each API in which they want a different behaviour than the default. Then they can reintroduce the `SWIFT_RETURNED_AS_(UN)RETAINED_BY_DEFAULT` at the type level to suppress the warnings on remaining unannotated APIs. A global default ownership convention (like always return `unretained`/`unowned`) was considered but it would weaken the diagnostic signal and remove valuable guardrails that help detect use-after-free bugs and memory leaks in absence of `SWIFT_RETURNS_(UN)RETAINED` annotations. In the absence of these annotations when Swift emits the unannotated API warning, the current fallback behavior (e.g. relying on heuristics based on API name such as `"create"`, `"copy"`, `"get"`) is derived from Objective-C interop but is ill-suited for C++, which has no consistent naming patterns for ownership semantics. Several codebases are expected to have project-specific conventions, such as defaulting to unretained except for factory methods and constructors. A type-level default seems like the most precise and scalable mechanism to support such patterns. It integrates cleanly with existing `SWIFT_SHARED_REFERENCE` usage and provides a per-type opt-in mechanism without global silencing of ownership diagnostics. This addition improves ergonomics while preserving the safety benefits of explicit annotations and diagnostics. rdar://145453509
@swift-ci please test |
…swiftlang#81329) This patch removes the `SWIFT_RETURNED_AS_RETAINED_BY_DEFAULT` annotation while maintaining the support for `SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT`. These type-level annotations were initially introduced in [PR-81093](swiftlang#81093) to reduce the annotation burden in large C++ codebases where many C++ APIs returning `SWIFT_SHARED_REFERENCE` types are exposed to Swift. ### Motivation The original goal was to make C++ interop more ergonomic by allowing type-level defaults for ownership conventions for`SWIFT_SHARED_REFERENCE` types . However, defaulting to retained return values (+1) seems to be problematic and poses memory safety risks. ### Why we’re removing `SWIFT_RETURNED_AS_RETAINED_BY_DEFAULT` - **Memory safety risks:** Defaulting to retained can potentially lead to use-after-free bugs when the API implementation actually returns `unowned` (`+0`). These errors are subtle and can be hard to debug or discover, particularly in the absence of explicit API-level `SWIFT_RETURNS_(UN)RETAINED` annotations. - **Risky transitive behavior:** If a `SWIFT_SHARED_REFERENCE` type is annotated with `SWIFT_RETURNED_AS_RETAINED_BY_DEFAULT`, any new C++ API returning this type will inherit the retained behavior by default—even if the API's actual return behavior is unretained. Unless explicitly overridden with `SWIFT_RETURNS_UNRETAINED`, this can introduce a silent mismatch in ownership expectations and lead to use-after-free bugs. This is especially risky in large or evolving codebases where such defaults may be overlooked. - **Simpler multiple inheritance semantics:** With only one type-level default (`SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT`), we avoid complications that can arise when multiple base classes specify conflicting ownership defaults. This simplifies reasoning about behavior in class hierarchies and avoids ambiguity when Swift determines the ownership convention for inherited APIs. ### Why we’re keeping `SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT` - It still enables projects to suppress warnings for unannotated C++ APIs returning `SWIFT_SHARED_REFERENCE` types, helping to reduce noise while maintaining clarity. - It encourages explicitness for retained behavior. Developers must annotate retained return values with `SWIFT_RETURNS_RETAINED`, making ownership intent clearer and safer. - The worst-case outcome of assuming unretained when the return is actually retained is a memory leak, which is more tolerable and easier to debug than a use-after-free. - Having a single default mechanism improves clarity for documentation, diagnostics, and long-term maintenance of Swift/C++ interop code.
…lt-returns-unretained-final
…warnings were disabled
@swift-ci please test |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Explanation: This patch introduces
SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT
type-level annotation to reduce the annotation burden when adoptingSWIFT_RETURNS_RETAINED
andSWIFT_RETURNS_UNRETAINED
API level annotations in large C++ codebases. Previously, the Swift compiler emitted warnings for C++ APIs returningSWIFT_SHARED_REFERENCE
types without explicitSWIFT_RETURNS_(UN)RETAINED
annotations. While helpful, this could be overwhelming in large projects. With these new annotations, developers can specify a default ownership convention per type, which applies to all APIs returning that type unless overridden at the API level. Defaults are inherited by subclasses and allow selective opt-out via explicit API-level annotations. This strikes a balance between safety and usability — maintaining diagnostic coverage while reducing annotation burden.Issue: rdar://145453509
Risk: Low, doesn't affect existing users unless they start using these new
SWIFT_RETURED_AS_(UN)RETAINED annotations
Testing: Written lit tests and manually tested on some adopter and sample projects locally
Original PRs: PR-81093, PR-81329
Reviewer: @j-hui @egorzhdan @Xazax-hun