From fc1d877968e0bab2d2031a880fbb407ccfb9fa76 Mon Sep 17 00:00:00 2001 From: Fahad Nayyar Date: Mon, 24 Feb 2025 00:22:27 -0800 Subject: [PATCH] [cxx-interop] Default ownership convention as 3rd optional parameter of SWIFT_SHARED_REFERENCE annotation rdar://145453509 --- lib/ClangImporter/ImportDecl.cpp | 17 +++ .../SwiftBridging/swift/bridging | 24 +++- lib/SIL/IR/SILFunctionType.cpp | 38 +++++-- .../cxx-functions-and-methods-returning-frt.h | 104 ++++++++++++++++++ ...retained-unretained-attributes-error.swift | 2 + .../frt-retained-unretained-attributes.swift | 20 ++++ 6 files changed, 191 insertions(+), 14 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index c03d496f046ba..da1c0da4c714c 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3664,6 +3664,23 @@ namespace { unannotatedAPIWarningNeeded = true; } + if (const auto *returnPtrTy = retType->getAs()) { + clang::QualType returnPointeeType = returnPtrTy->getPointeeType(); + if (const auto *returnRecordType = + returnPointeeType->getAs()) { + clang::RecordDecl *returnRecordDecl = returnRecordType->getDecl(); + for (const auto *attr : returnRecordDecl->getAttrs()) { + if (const auto *swiftAttr = + dyn_cast(attr)) { + if (swiftAttr->getAttribute() == "ownership:unretained" || + swiftAttr->getAttribute() == "ownership:retained") { + unannotatedAPIWarningNeeded = false; + } + } + } + } + } + if (unannotatedAPIWarningNeeded) { HeaderLoc loc(decl->getLocation()); Impl.diagnose(loc, diag::no_returns_retained_returns_unretained, diff --git a/lib/ClangImporter/SwiftBridging/swift/bridging b/lib/ClangImporter/SwiftBridging/swift/bridging index c111d82ad884d..6f9850d67a5b6 100644 --- a/lib/ClangImporter/SwiftBridging/swift/bridging +++ b/lib/ClangImporter/SwiftBridging/swift/bridging @@ -45,7 +45,19 @@ #define _CXX_INTEROP_CONCAT(...) \ _CXX_INTEROP_CONCAT_(__VA_ARGS__,,,,,,,,,,,,,,,,,) -/// Specifies that a C++ `class` or `struct` is reference-counted using +#define SWIFT_SHARED_REFERENCE_2ARGS(_retain, _release) \ + __attribute__((swift_attr("import_reference"))) \ + __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \ + __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:_release)))) + +#define SWIFT_SHARED_REFERENCE_3ARGS(_retain, _release, _ownership) \ + __attribute__((swift_attr("import_reference"))) \ + __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \ + __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:_release)))) \ + __attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(ownership:_ownership)))) + +// FN_TODO: modify the documentation + /// 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. @@ -71,10 +83,10 @@ /// 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)))) +#define _CXX_INTEROP_GET_MACRO(_1, _2, _3, NAME, ...) NAME +#define SWIFT_SHARED_REFERENCE(...) \ + _CXX_INTEROP_GET_MACRO(__VA_ARGS__, SWIFT_SHARED_REFERENCE_3ARGS, \ + SWIFT_SHARED_REFERENCE_2ARGS)(__VA_ARGS__) /// 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 @@ -232,7 +244,7 @@ // 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_SHARED_REFERENCE(_retain, _release, ...) #define SWIFT_IMMORTAL_REFERENCE #define SWIFT_UNSAFE_REFERENCE #define SWIFT_NAME(_name) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index a52a1608532bd..b7e78fb2e3677 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -1346,17 +1346,39 @@ class Conventions { } // Determines owned/unowned ResultConvention of the returned value based on - // returns_retained/returns_unretained attribute. + // SWIFT_RETURNS_(UN)RETAINED annotation on the C++ API and then based on + // ownership:(un)retained as the 3rd optional parameter of + // SWIFT_SHARED_REFERENCE annotation on the returned type std::optional getCxxRefConventionWithAttrs(const TypeLowering &tl, const clang::Decl *decl) const { - if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) { - for (const auto *attr : decl->getAttrs()) { - if (const auto *swiftAttr = dyn_cast(attr)) { - if (swiftAttr->getAttribute() == "returns_unretained") { - return ResultConvention::Unowned; - } else if (swiftAttr->getAttribute() == "returns_retained") { - return ResultConvention::Owned; + if (tl.getLoweredType().isForeignReferenceType()) { + if (decl->hasAttrs()) { + for (const auto *attr : decl->getAttrs()) { + if (const auto *swiftAttr = dyn_cast(attr)) { + if (swiftAttr->getAttribute() == "returns_unretained") { + return ResultConvention::Unowned; + } else if (swiftAttr->getAttribute() == "returns_retained") { + return ResultConvention::Owned; + } + } + } + } + if (auto *classDecl = tl.getLoweredType() + .getASTType() + .getPointer() + ->lookThroughAllOptionalTypes() + ->getClassOrBoundGenericClass()) { + if (auto clangRecordDecl = + dyn_cast(classDecl->getClangDecl())) { + for (const auto *attr : clangRecordDecl->getAttrs()) { + if (const auto *swiftAttr = dyn_cast(attr)) { + if (swiftAttr->getAttribute() == "ownership:unretained") { + return ResultConvention::Unowned; + } else if (swiftAttr->getAttribute() == "ownership:retained") { + return ResultConvention::Owned; + } + } } } } diff --git a/test/Interop/Cxx/foreign-reference/Inputs/cxx-functions-and-methods-returning-frt.h b/test/Interop/Cxx/foreign-reference/Inputs/cxx-functions-and-methods-returning-frt.h index a53510a9e57e6..999d68cbdde3c 100644 --- a/test/Interop/Cxx/foreign-reference/Inputs/cxx-functions-and-methods-returning-frt.h +++ b/test/Interop/Cxx/foreign-reference/Inputs/cxx-functions-and-methods-returning-frt.h @@ -1,4 +1,5 @@ #pragma once +#include "visibility.h" #include // FRT or SWIFT_SHARED_REFERENCE type @@ -326,3 +327,106 @@ struct Derived : Base { FRTStruct *_Nonnull VirtualMethodReturningFRTUnowned() override __attribute__((swift_attr("returns_unretained"))); }; + +SWIFT_BEGIN_NULLABILITY_ANNOTATIONS + +namespace DefaultOwnershipConventionOnCXXForegnRefType { +struct __attribute__((swift_attr("import_reference"))) +__attribute__((swift_attr("retain:defRetain1"))) +__attribute__((swift_attr("release:defRelease1"))) +__attribute__((swift_attr("ownership:retained"))) RefTypeDefaultRetained {}; + +RefTypeDefaultRetained *returnRefTypeDefaultRetained() { + return new RefTypeDefaultRetained(); +} + +struct __attribute__((swift_attr("import_reference"))) +__attribute__((swift_attr("retain:defRetain2"))) +__attribute__((swift_attr("release:defRelease2"))) +__attribute__((swift_attr("ownership:unretained"))) RefTypeDefaultUnretained {}; + +RefTypeDefaultUnretained *returnRefTypeDefaultUnretained() { + return new RefTypeDefaultUnretained(); +} +} // namespace DefaultOwnershipConventionOnCXXForegnRefType +void defRetain1( + DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultRetained *v) {}; +void defRelease1( + DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultRetained *v) {}; + +void defRetain2( + DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultUnretained *v) { +}; +void defRelease2( + DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultUnretained *v) { +}; + +namespace FunctionAnnotationHasPrecedence { +struct __attribute__((swift_attr("import_reference"))) +__attribute__((swift_attr("retain:defaultRetain1"))) +__attribute__((swift_attr("release:defaultRelease1"))) +__attribute__((swift_attr("ownership:unretained"))) RefTypeDefaultUnRetained {}; + +RefTypeDefaultUnRetained *returnRefTypeDefaultUnRetained() { + return new RefTypeDefaultUnRetained(); +} +RefTypeDefaultUnRetained *returnRefTypeDefaultUnRetainedAnnotatedRetained() + __attribute__((swift_attr("returns_retained"))) { + return new RefTypeDefaultUnRetained(); +} + +struct __attribute__((swift_attr("import_reference"))) +__attribute__((swift_attr("retain:defaultRetain2"))) +__attribute__((swift_attr("release:defaultRelease2"))) +__attribute__((swift_attr("ownership:retained"))) RefTypeDefaultRetained {}; + +RefTypeDefaultRetained *returnRefTypeDefaultRetained() { + return new RefTypeDefaultRetained(); +} +RefTypeDefaultRetained *returnRefTypeDefaultRetainedAnnotatedUnRetained() + __attribute__((swift_attr("returns_unretained"))) { + return new RefTypeDefaultRetained(); +} + +} // namespace FunctionAnnotationHasPrecedence + +void defaultRetain1( + FunctionAnnotationHasPrecedence::RefTypeDefaultUnRetained *v) {}; +void defaultRelease1( + FunctionAnnotationHasPrecedence::RefTypeDefaultUnRetained *v) {}; + +void defaultRetain2( + FunctionAnnotationHasPrecedence::RefTypeDefaultRetained *v) {}; +void defaultRelease2( + FunctionAnnotationHasPrecedence::RefTypeDefaultRetained *v) {}; + +namespace DefaultOwnershipSuppressUnannotatedAPIWarning { + +struct __attribute__((swift_attr("import_reference"))) +__attribute__((swift_attr("retain:rretain"))) +__attribute__((swift_attr("release:rrelease"))) RefType {}; + +RefType *returnRefType() { return new RefType(); } + +struct __attribute__((swift_attr("import_reference"))) +__attribute__((swift_attr("retain:dretain"))) +__attribute__((swift_attr("release:drelease"))) +__attribute__((swift_attr("ownership:retained"))) RefTypeDefaultRetained {}; + +RefTypeDefaultRetained *returnRefTypeDefaultRetained() { + return new RefTypeDefaultRetained(); +} + +} // namespace DefaultOwnershipSuppressUnannotatedAPIWarning + +void rretain(DefaultOwnershipSuppressUnannotatedAPIWarning::RefType *v) {}; +void rrelease(DefaultOwnershipSuppressUnannotatedAPIWarning::RefType *v) {}; + +void dretain( + DefaultOwnershipSuppressUnannotatedAPIWarning::RefTypeDefaultRetained *v) { +}; +void drelease( + DefaultOwnershipSuppressUnannotatedAPIWarning::RefTypeDefaultRetained *v) { +}; + +SWIFT_END_NULLABILITY_ANNOTATIONS \ No newline at end of file diff --git a/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes-error.swift b/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes-error.swift index 7d1eac34a8328..332c5087616b6 100644 --- a/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes-error.swift +++ b/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes-error.swift @@ -32,3 +32,5 @@ let f = FunctionVoidToFRTStruct() let frt = f() let nonFrt = NonFRTStruct() let nonFrtLocalVar1 = global_function_returning_templated_retrun_frt_owned(nonFrt) +let _ = DefaultOwnershipSuppressUnannotatedAPIWarning.returnRefType() +let _ = DefaultOwnershipSuppressUnannotatedAPIWarning.returnRefTypeDefaultRetained() diff --git a/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes.swift b/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes.swift index 4d4cd73968b64..d55cf6a227849 100644 --- a/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes.swift +++ b/test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes.swift @@ -247,3 +247,23 @@ func testVirtualMethods(base: Base, derived: Derived) { var frt4 = mutableDerived.VirtualMethodReturningFRTOwned() // CHECK: function_ref @{{.*}}VirtualMethodReturningFRTOwned{{.*}} : $@convention(cxx_method) (@inout Derived) -> @owned FRTStruct } + +func testDefaultOwnershipAnnotation() { + let _ = DefaultOwnershipConventionOnCXXForegnRefType.returnRefTypeDefaultRetained() + // CHECK: function_ref {{.*}}returnRefTypeDefaultRetained{{.*}} : $@convention(c) () -> @owned DefaultOwnershipConventionOnCXXForegnRefType.RefTypeDefaultRetained + + let _ = DefaultOwnershipConventionOnCXXForegnRefType.returnRefTypeDefaultUnretained() + // CHECK: function_ref {{.*}}returnRefTypeDefaultUnretained{{.*}} : $@convention(c) () -> DefaultOwnershipConventionOnCXXForegnRefType.RefTypeDefaultUnretained + + let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultUnRetained() + // CHECK: function_ref {{.*}}returnRefTypeDefaultUnRetained{{.*}} : $@convention(c) () -> FunctionAnnotationHasPrecedence.RefTypeDefaultUnRetained + + let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultUnRetainedAnnotatedRetained() + // CHECK: function_ref {{.*}}returnRefTypeDefaultUnRetainedAnnotatedRetained{{.*}} : $@convention(c) () -> @owned FunctionAnnotationHasPrecedence.RefTypeDefaultUnRetained + + let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultRetained() + // CHECK: function_ref {{.*}}returnRefTypeDefaultRetained{{.*}} : $@convention(c) () -> @owned FunctionAnnotationHasPrecedence.RefTypeDefaultRetained + + let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultRetainedAnnotatedUnRetained() + // CHECK: function_ref {{.*}}returnRefTypeDefaultRetainedAnnotatedUnRetained{{.*}} : $@convention(c) () -> FunctionAnnotationHasPrecedence.RefTypeDefaultRetained +}