Skip to content

Commit f852d7a

Browse files
authored
Merge pull request swiftlang#78484 from swiftlang/fahadnayyar/6.1-returns-retained-objc-support
🍒 [cxx-interop] Add support for SWIFT_RETURNS_(UN)RETAINED for ObjC APIs returning C++ FRT
2 parents b8e183e + c66996c commit f852d7a

File tree

4 files changed

+82
-29
lines changed

4 files changed

+82
-29
lines changed

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,25 @@ class Conventions {
12981298
}
12991299
llvm_unreachable("unhandled ownership");
13001300
}
1301+
1302+
// Determines owned/unowned ResultConvention of the returned value based on
1303+
// returns_retained/returns_unretained attribute.
1304+
std::optional<ResultConvention>
1305+
getCxxRefConventionWithAttrs(const TypeLowering &tl,
1306+
const clang::Decl *decl) const {
1307+
if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) {
1308+
for (const auto *attr : decl->getAttrs()) {
1309+
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
1310+
if (swiftAttr->getAttribute() == "returns_unretained") {
1311+
return ResultConvention::Unowned;
1312+
} else if (swiftAttr->getAttribute() == "returns_retained") {
1313+
return ResultConvention::Owned;
1314+
}
1315+
}
1316+
}
1317+
}
1318+
return std::nullopt;
1319+
}
13011320
};
13021321

13031322
/// A visitor for breaking down formal result types into a SILResultInfo
@@ -3335,7 +3354,8 @@ class ObjCMethodConventions : public Conventions {
33353354
return ResultConvention::Owned;
33363355

33373356
if (tl.getLoweredType().isForeignReferenceType())
3338-
return ResultConvention::Unowned;
3357+
return getCxxRefConventionWithAttrs(tl, Method)
3358+
.value_or(ResultConvention::Unowned);
33393359

33403360
return ResultConvention::Autoreleased;
33413361
}
@@ -3375,25 +3395,6 @@ class CFunctionTypeConventions : public Conventions {
33753395
const clang::FunctionType *type)
33763396
: Conventions(kind), FnType(type) {}
33773397

3378-
// Determines owned/unowned ResultConvention of the returned value based on
3379-
// returns_retained/returns_unretained attribute.
3380-
std::optional<ResultConvention>
3381-
getForeignReferenceTypeResultConventionWithAttributes(
3382-
const TypeLowering &tl, const clang::FunctionDecl *decl) const {
3383-
if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) {
3384-
for (const auto *attr : decl->getAttrs()) {
3385-
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3386-
if (swiftAttr->getAttribute() == "returns_unretained") {
3387-
return ResultConvention::Unowned;
3388-
} else if (swiftAttr->getAttribute() == "returns_retained") {
3389-
return ResultConvention::Owned;
3390-
}
3391-
}
3392-
}
3393-
}
3394-
return std::nullopt;
3395-
}
3396-
33973398
public:
33983399
CFunctionTypeConventions(const clang::FunctionType *type)
33993400
: Conventions(ConventionsKind::CFunctionType), FnType(type) {}
@@ -3511,11 +3512,7 @@ class CFunctionConventions : public CFunctionTypeConventions {
35113512
return ResultConvention::Indirect;
35123513
}
35133514

3514-
// Explicitly setting the ownership of the returned FRT if the C++
3515-
// global/free function has either swift_attr("returns_retained") or
3516-
// ("returns_unretained") attribute.
3517-
if (auto resultConventionOpt =
3518-
getForeignReferenceTypeResultConventionWithAttributes(tl, TheDecl))
3515+
if (auto resultConventionOpt = getCxxRefConventionWithAttrs(tl, TheDecl))
35193516
return *resultConventionOpt;
35203517

35213518
if (isCFTypedef(tl, TheDecl->getReturnType())) {
@@ -3597,11 +3594,8 @@ class CXXMethodConventions : public CFunctionTypeConventions {
35973594
return ResultConvention::Indirect;
35983595
}
35993596

3600-
// Explicitly setting the ownership of the returned FRT if the C++ member
3601-
// method has either swift_attr("returns_retained") or
3602-
// ("returns_unretained") attribute.
36033597
if (auto resultConventionOpt =
3604-
getForeignReferenceTypeResultConventionWithAttributes(resultTL, TheDecl))
3598+
getCxxRefConventionWithAttrs(resultTL, TheDecl))
36053599
return *resultConventionOpt;
36063600

36073601
if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <Foundation/Foundation.h>
2+
3+
#pragma clang diagnostic push
4+
#pragma clang diagnostic ignored "-Wnullability-extension"
5+
#pragma clang assume_nonnull begin
6+
7+
struct CxxRefType {
8+
} __attribute__((swift_attr("import_reference")))
9+
__attribute__((swift_attr("retain:retainCxxRefType")))
10+
__attribute__((swift_attr("release:releaseCxxRefType")));
11+
12+
void retainCxxRefType(CxxRefType *_Nonnull b) {}
13+
void releaseCxxRefType(CxxRefType *_Nonnull b) {}
14+
15+
@interface Bridge : NSObject
16+
17+
+ (struct CxxRefType *)objCMethodReturningFRTUnannotated;
18+
+ (struct CxxRefType *)objCMethodReturningFRTUnowned
19+
__attribute__((swift_attr("returns_unretained")));
20+
+ (struct CxxRefType *)objCMethodReturningFRTOwned
21+
__attribute__((swift_attr("returns_retained")));
22+
23+
@end
24+
25+
@implementation Bridge
26+
+ (struct CxxRefType *)objCMethodReturningFRTUnannotated {
27+
};
28+
+ (struct CxxRefType *)objCMethodReturningFRTUnowned
29+
__attribute__((swift_attr("returns_unretained"))) {
30+
}
31+
+ (struct CxxRefType *)objCMethodReturningFRTOwned
32+
__attribute__((swift_attr("returns_retained"))) {
33+
}
34+
35+
@end
36+
37+
#pragma clang diagnostic pop
38+
#pragma clang assume_nonnull end

test/Interop/Cxx/objc-correctness/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,8 @@ module ID {
4444
requires objc
4545
requires cplusplus
4646
}
47+
48+
module CxxForeignRef {
49+
header "cxx-frt.h"
50+
requires cplusplus
51+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-emit-sil -I %S/Inputs -cxx-interoperability-mode=upcoming-swift -disable-availability-checking -diagnostic-style llvm %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
2+
3+
import CxxForeignRef
4+
5+
// REQUIRES: objc_interop
6+
7+
func testObjCMethods() {
8+
var frt1 = Bridge.objCMethodReturningFRTUnannotated()
9+
// CHECK: objc_method {{.*}} #Bridge.objCMethodReturningFRTUnannotated!foreign : (Bridge.Type) -> () -> CxxRefType, $@convention(objc_method) (@objc_metatype Bridge.Type) -> CxxRefType
10+
11+
var frt2 = Bridge.objCMethodReturningFRTUnowned()
12+
// CHECK: objc_method {{.*}} #Bridge.objCMethodReturningFRTUnowned!foreign : (Bridge.Type) -> () -> CxxRefType, $@convention(objc_method) (@objc_metatype Bridge.Type) -> CxxRefType
13+
14+
var frt3 = Bridge.objCMethodReturningFRTOwned()
15+
// CHECK: objc_method {{.*}} #Bridge.objCMethodReturningFRTOwned!foreign : (Bridge.Type) -> () -> CxxRefType, $@convention(objc_method) (@objc_metatype Bridge.Type) -> @owned CxxRefType
16+
}

0 commit comments

Comments
 (0)