Skip to content

Commit 426d471

Browse files
committed
[cxx-interop] Add support for SWIFT_RETURNS_(UN)RETAINED for ObjC APIs returning C++ FRT
rdar://135360972
1 parent 8553f99 commit 426d471

File tree

5 files changed

+82
-30
lines changed

5 files changed

+82
-30
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
@@ -3341,7 +3360,8 @@ class ObjCMethodConventions : public Conventions {
33413360
return ResultConvention::Owned;
33423361

33433362
if (tl.getLoweredType().isForeignReferenceType())
3344-
return ResultConvention::Unowned;
3363+
return getCxxRefConventionWithAttrs(tl, Method)
3364+
.value_or(ResultConvention::Unowned);
33453365

33463366
return ResultConvention::Autoreleased;
33473367
}
@@ -3381,25 +3401,6 @@ class CFunctionTypeConventions : public Conventions {
33813401
const clang::FunctionType *type)
33823402
: Conventions(kind), FnType(type) {}
33833403

3384-
// Determines owned/unowned ResultConvention of the returned value based on
3385-
// returns_retained/returns_unretained attribute.
3386-
std::optional<ResultConvention>
3387-
getForeignReferenceTypeResultConventionWithAttributes(
3388-
const TypeLowering &tl, const clang::FunctionDecl *decl) const {
3389-
if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) {
3390-
for (const auto *attr : decl->getAttrs()) {
3391-
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3392-
if (swiftAttr->getAttribute() == "returns_unretained") {
3393-
return ResultConvention::Unowned;
3394-
} else if (swiftAttr->getAttribute() == "returns_retained") {
3395-
return ResultConvention::Owned;
3396-
}
3397-
}
3398-
}
3399-
}
3400-
return std::nullopt;
3401-
}
3402-
34033404
public:
34043405
CFunctionTypeConventions(const clang::FunctionType *type)
34053406
: Conventions(ConventionsKind::CFunctionType), FnType(type) {}
@@ -3517,11 +3518,7 @@ class CFunctionConventions : public CFunctionTypeConventions {
35173518
return ResultConvention::Indirect;
35183519
}
35193520

3520-
// Explicitly setting the ownership of the returned FRT if the C++
3521-
// global/free function has either swift_attr("returns_retained") or
3522-
// ("returns_unretained") attribute.
3523-
if (auto resultConventionOpt =
3524-
getForeignReferenceTypeResultConventionWithAttributes(tl, TheDecl))
3521+
if (auto resultConventionOpt = getCxxRefConventionWithAttrs(tl, TheDecl))
35253522
return *resultConventionOpt;
35263523

35273524
if (isCFTypedef(tl, TheDecl->getReturnType())) {
@@ -3603,11 +3600,8 @@ class CXXMethodConventions : public CFunctionTypeConventions {
36033600
return ResultConvention::Indirect;
36043601
}
36053602

3606-
// Explicitly setting the ownership of the returned FRT if the C++ member
3607-
// method has either swift_attr("returns_retained") or
3608-
// ("returns_unretained") attribute.
36093603
if (auto resultConventionOpt =
3610-
getForeignReferenceTypeResultConventionWithAttributes(resultTL, TheDecl))
3604+
getCxxRefConventionWithAttrs(resultTL, TheDecl))
36113605
return *resultConventionOpt;
36123606

36133607
if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&

test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,4 +245,3 @@ func testVirtualMethods(base: Base, derived: Derived) {
245245
var frt4 = mutableDerived.VirtualMethodReturningFRTOwned()
246246
// CHECK: function_ref @{{.*}}VirtualMethodReturningFRTOwned{{.*}} : $@convention(cxx_method) (@inout Derived) -> @owned FRTStruct
247247
}
248-
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)