Files
fahadnayyar b311c63ea9 [cxx-interop] Remove SWIFT_RETURNED_AS_RETAINED_BY_DEFAULT annotation (#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](https://github.com/swiftlang/swift/pull/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.
2025-05-07 13:25:25 -07:00

273 lines
11 KiB
Plaintext

// -*- C++ -*-
//===------------------ bridging - C++ and Swift Interop --------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides common utilities and annotations that are useful for C++
// codebases that interoperate with Swift.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H
#define SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H
#ifdef __has_attribute
#define _CXX_INTEROP_HAS_ATTRIBUTE(x) __has_attribute(x)
#else
#define _CXX_INTEROP_HAS_ATTRIBUTE(x) 0
#endif
#if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
/// Specifies that a C++ `class` or `struct` owns and controls the lifetime of all
/// of the objects it references. Such type should not reference any objects whose
/// lifetime is controlled externally. This annotation allows Swift to import methods
/// that return a `class` or `struct` type that is annotated with this macro.
#define SWIFT_SELF_CONTAINED __attribute__((swift_attr("import_owned")))
/// Specifies that a C++ method returns a value that is presumed to contain
/// objects whose lifetime is not dependent on `this` or other parameters passed
/// to the method.
#define SWIFT_RETURNS_INDEPENDENT_VALUE __attribute__((swift_attr("import_unsafe")))
#define _CXX_INTEROP_STRINGIFY(_x) #_x
#define _CXX_INTEROP_CONCAT_(a,b,c,d,e,f,g,i,j,k,l,m,n,o,p,...) \
#a "," #b "," #c "," #d "," #e "," #f "," #g "," #i "," #j "," #k "," \
#l "," #m "," #n "," #o "," #p
#define _CXX_INTEROP_CONCAT(...) \
_CXX_INTEROP_CONCAT_(__VA_ARGS__,,,,,,,,,,,,,,,,,)
/// Specifies that a C++ `class` or `struct` is reference-counted using
/// the given `retain` and `release` functions. This annotation lets Swift import
/// such a type as reference counted type in Swift, taking advantage of Swift's
/// automatic reference counting.
///
/// This example shows how to use this macro to let Swift know that
/// a non-copyable reference counted C++ class can be imported as a reference counted type in Swift:
/// ```c++
/// class SWIFT_SHARED_REFERENCE(retainSharedObject, releaseSharedObject)
/// SharedObject : NonCopyable, IntrusiveReferenceCounted<SharedObject> {
/// public:
/// static SharedObject* create();
/// void doSomething();
/// };
///
/// void retainSharedObject(SharedObject *);
/// void releaseSharedObject(SharedObject *);
/// ```
///
/// Then, the Swift programmer would be able to use it in the following manner:
///
/// ```swift
/// let object = SharedObject.create()
/// object.doSomething()
/// // The Swift compiler will release object here.
/// ```
#define SWIFT_SHARED_REFERENCE(_retain, _release) \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:_release))))
/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime
/// is presumed to be immortal, i.e. the reference to such object is presumed to
/// always be valid. This annotation lets Swift import such a type as a reference
/// type in Swift.
////
/// This example shows how to use this macro to let Swift know that
/// a non-copyable singleton C++ class can be imported as a reference type in Swift:
/// ```c++
/// class SWIFT_IMMORTAL_REFERENCE
/// LoggerSingleton : NonCopyable {
/// public:
/// static LoggerSingleton &getInstance();
/// void log(int x);
/// };
/// ```
///
/// Then, the Swift programmer would be able to use it in the following manner:
///
/// ```swift
/// let logger = LoggerSingleton.getInstance()
/// logger.log(123)
/// ```
#define SWIFT_IMMORTAL_REFERENCE \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:immortal)))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:immortal))))
/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime
/// is not managed automatically. The programmer must validate that any reference
/// to such object is valid themselves. This annotation lets Swift import such a type as a reference type in Swift.
#define SWIFT_UNSAFE_REFERENCE \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:immortal)))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:immortal)))) \
__attribute__((swift_attr("unsafe")))
/// Specifies a name that will be used in Swift for this declaration instead of its original name.
#define SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
/// Specifies that a specific C++ `class` or `struct` conforms to a
/// a specific Swift protocol.
///
/// This example shows how to use this macro to conform a class template to a Swift protocol:
/// ```
/// template<class T>
/// class SWIFT_CONFORMS_TO_PROTOCOL(SwiftModule.ProtocolName)
/// CustomClass {};
/// ```
#define SWIFT_CONFORMS_TO_PROTOCOL(_moduleName_protocolName) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(conforms_to:_moduleName_protocolName))))
/// Specifies that a specific C++ method should be imported as a computed
/// property. If this macro is specified on a getter, a getter will be
/// synthesized. If this macro is specified on a setter, both a getter and
/// setter will be synthesized.
///
/// For example:
/// ```
/// int getX() SWIFT_COMPUTED_PROPERTY;
/// ```
/// Will be imported as `var x: CInt {...}`.
#define SWIFT_COMPUTED_PROPERTY \
__attribute__((swift_attr("import_computed_property")))
/// Specifies that a specific **constant** C++ member function should be imported as
/// `mutating` Swift method. This annotation should be added to constant C++ member functions
/// that mutate `mutable` fields in a C++ object, to let Swift know that this function is still mutating
/// and thus that it should become a `mutating` method in Swift.
#define SWIFT_MUTATING \
__attribute__((swift_attr("mutating")))
/// Specifies that a specific c++ type such class or struct should be imported as type marked
/// as `@unchecked Sendable` type in swift. If this annotation is used, the type is therefore allowed to
/// use safely across async contexts.
///
/// For example
/// ```
/// class SWIFT_UNCHECKED_SENDABLE CustomUserType
/// { ... }
/// ```
/// Will be imported as `struct CustomUserType: @unchecked Sendable`
#define SWIFT_UNCHECKED_SENDABLE \
__attribute__((swift_attr("@Sendable")))
/// Specifies that a specific c++ type such class or struct should be imported
/// as a non-copyable Swift value type.
#define SWIFT_NONCOPYABLE \
__attribute__((swift_attr("~Copyable")))
/// Specifies that a specific c++ type such class or struct should be imported
/// as a non-escapable Swift value type when the non-escapable language feature
/// is enabled.
#define SWIFT_NONESCAPABLE \
__attribute__((swift_attr("~Escapable")))
/// Specifies that a specific c++ type such class or struct should be imported
/// as a escapable Swift value. While this matches the default behavior,
/// in safe mode interop mode it ensures that the type is not marked as
/// unsafe.
#define SWIFT_ESCAPABLE \
__attribute__((swift_attr("Escapable")))
/// Specifies that a C++ `class` or `struct` should be imported as a escapable
/// Swift value if all of the specified template arguments are escapable.
#define SWIFT_ESCAPABLE_IF(...) \
__attribute__((swift_attr("escapable_if:" _CXX_INTEROP_CONCAT(__VA_ARGS__))))
/// Specifies that the return value is passed as owned for C++ functions and
/// methods returning types annotated as `SWIFT_SHARED_REFERENCE`
#define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained")))
/// Specifies that the return value is passed as unowned for C++ functions and
/// methods returning types annotated as `SWIFT_SHARED_REFERENCE`
#define SWIFT_RETURNS_UNRETAINED \
__attribute__((swift_attr("returns_unretained")))
/// Applied to a C++ foreign reference type annotated with
/// SWIFT_SHARED_REFERENCE. Indicates that C++ APIs returning this type are
/// assumed to return an unowned (+0) value by default, unless explicitly annotated
/// with SWIFT_RETURNS_RETAINED.
///
/// For example:
/// ```c++
/// struct SWIFT_SHARED_REFERENCE(retainBar, releaseBar)
/// SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT
/// Bar { ... };
/// ```
///
/// In Swift, C++ APIs returning `Bar*` will be assumed to return an unowned
/// value.
#define SWIFT_RETURNED_AS_UNRETAINED_BY_DEFAULT \
__attribute__((swift_attr("returned_as_unretained_by_default")))
/// Specifies that the non-public members of a C++ class, struct, or union can
/// be accessed from extensions of that type, in the given file ID.
///
/// In other words, Swift's access controls will behave as if the non-public
/// members of the annotated C++ class were privated declared in the specified
/// Swift source file, rather than in a C++ header file/Clang module.
///
/// For example, we can annotate a C++ class definition like this:
///
/// ```c++
/// class SWIFT_PRIVATE_FILEID("MySwiftModule/MySwiftFile.swift")
/// MyCxxClass {
/// private:
/// void privateMethod();
/// int privateStorage;
/// };
/// ```
///
/// Then, Swift extensions of `MyCxxClass` in `MySwiftModule/MySwiftFile.swift`
/// are allowed to access `privateMethod()` and `privateStorage`:
///
/// ```swift
/// //-- MySwiftModule/SwiftFile.swift
/// extension MyCxxClass {
/// func ext() {
/// privateMethod()
/// print("\(privateStorage)")
/// }
/// }
/// ```
///
/// Non-public access is still forbidden outside of extensions and outside of
/// the designated file ID.
#define SWIFT_PRIVATE_FILEID(_fileID) \
__attribute__((swift_attr("private_fileid:" _fileID)))
#else // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
// Empty defines for compilers that don't support `attribute(swift_attr)`.
#define SWIFT_SELF_CONTAINED
#define SWIFT_RETURNS_INDEPENDENT_VALUE
#define SWIFT_SHARED_REFERENCE(_retain, _release)
#define SWIFT_IMMORTAL_REFERENCE
#define SWIFT_UNSAFE_REFERENCE
#define SWIFT_NAME(_name)
#define SWIFT_CONFORMS_TO_PROTOCOL(_moduleName_protocolName)
#define SWIFT_COMPUTED_PROPERTY
#define SWIFT_MUTATING
#define SWIFT_UNCHECKED_SENDABLE
#define SWIFT_NONCOPYABLE
#define SWIFT_NONESCAPABLE
#define SWIFT_ESCAPABLE
#define SWIFT_ESCAPABLE_IF(...)
#define SWIFT_RETURNS_RETAINED
#define SWIFT_RETURNS_UNRETAINED
#define SWIFT_PRIVATE_FILEID(_fileID)
#endif // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
#undef _CXX_INTEROP_HAS_ATTRIBUTE
#endif // SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H