Skip to content

Commit 1f150a5

Browse files
committed
[cxx-interop] Add SWIFT_RETURNS_RETAINED and SWIFT_RETURNS_UNRETAINED annotations to specify ownership of FRT returned by a C++ method or function
1 parent f0ce346 commit 1f150a5

File tree

8 files changed

+259
-0
lines changed

8 files changed

+259
-0
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ ERROR(failed_base_method_call_synthesis,none,
248248
"failed to synthesize call to the base method %0 of type %0",
249249
(ValueDecl *, ValueDecl *))
250250

251+
ERROR(both_returns_retained_returns_unretained,none,
252+
"%0 cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes", (const clang::NamedDecl*))
253+
251254
NOTE(unsupported_builtin_type, none, "built-in type '%0' not supported", (StringRef))
252255
NOTE(record_field_not_imported, none, "field %0 unavailable (cannot import)", (const clang::NamedDecl*))
253256
NOTE(invoked_func_not_imported, none, "function %0 unavailable (cannot import)", (const clang::NamedDecl*))

lib/ClangImporter/ImportDecl.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3256,6 +3256,28 @@ namespace {
32563256
return property->getParsedAccessor(AccessorKind::Set);
32573257
}
32583258

3259+
// Emit error if a decl is annotated with both
3260+
// swift_attr("returns_retained") and swift_attr("returns_unretained")
3261+
if (decl->hasAttrs()) {
3262+
bool returnsRetainedAttrIsPresent = false;
3263+
bool returnsUnretainedAttrIsPresent = false;
3264+
for (const auto *attr : decl->getAttrs()) {
3265+
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3266+
if (swiftAttr->getAttribute() == "returns_unretained") {
3267+
returnsUnretainedAttrIsPresent = true;
3268+
} else if (swiftAttr->getAttribute() == "returns_retained") {
3269+
returnsRetainedAttrIsPresent = true;
3270+
}
3271+
}
3272+
}
3273+
3274+
if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) {
3275+
HeaderLoc loc(decl->getLocation());
3276+
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
3277+
decl);
3278+
}
3279+
}
3280+
32593281
return importFunctionDecl(decl, importedName, correctSwiftName,
32603282
std::nullopt);
32613283
}

lib/ClangImporter/SwiftBridging/swift/bridging

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@
158158
#define SWIFT_NONCOPYABLE \
159159
__attribute__((swift_attr("~Copyable")))
160160

161+
/// Specifies the ownership semantics (passed as owned/retained or
162+
/// unowned/unretained) for C++ free/global functions and static methods having
163+
/// a FRT as return type.
164+
#define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained")))
165+
#define SWIFT_RETURNS_UNRETAINED \
166+
__attribute__((swift_attr("returns_unretained")))
167+
161168
#else // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
162169

163170
// Empty defines for compilers that don't support `attribute(swift_attr)`.
@@ -172,6 +179,8 @@
172179
#define SWIFT_MUTATING
173180
#define SWIFT_UNCHECKED_SENDABLE
174181
#define SWIFT_NONCOPYABLE
182+
#define SWIFT_RETURNS_RETAINED
183+
#define SWIFT_RETURNS_UNRETAINED
175184

176185
#endif // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
177186

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3423,6 +3423,26 @@ class CFunctionTypeConventions : public Conventions {
34233423
const clang::FunctionType *type)
34243424
: Conventions(kind), FnType(type) {}
34253425

3426+
// Helper function to return ResultConvention if the decl has either
3427+
// swift_attr("returns_retained") or ("returns_unretained") attribute.
3428+
std::optional<ResultConvention>
3429+
getResultConventionWithAttributes(const TypeLowering &tl,
3430+
const clang::FunctionDecl *decl) const {
3431+
if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) {
3432+
for (const auto *attr : decl->getAttrs()) {
3433+
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3434+
if (swiftAttr->getAttribute() == "returns_unretained") {
3435+
return ResultConvention::Unowned;
3436+
} else if (swiftAttr->getAttribute() == "returns_retained") {
3437+
return ResultConvention::Owned;
3438+
}
3439+
}
3440+
}
3441+
}
3442+
return std::nullopt; // Return an empty optional if no matching attribute is
3443+
// found
3444+
}
3445+
34263446
public:
34273447
CFunctionTypeConventions(const clang::FunctionType *type)
34283448
: Conventions(ConventionsKind::CFunctionType), FnType(type) {}
@@ -3540,6 +3560,13 @@ class CFunctionConventions : public CFunctionTypeConventions {
35403560
return ResultConvention::Indirect;
35413561
}
35423562

3563+
// Explicitly setting the ownership of the returned FRT if the C++
3564+
// global/free function has either swift_attr("returns_retained") or
3565+
// ("returns_unretained") attribute.
3566+
if (auto resultConventionOpt =
3567+
getResultConventionWithAttributes(tl, TheDecl))
3568+
return *resultConventionOpt;
3569+
35433570
if (isCFTypedef(tl, TheDecl->getReturnType())) {
35443571
// The CF attributes aren't represented in the type, so we need
35453572
// to check them here.
@@ -3618,6 +3645,14 @@ class CXXMethodConventions : public CFunctionTypeConventions {
36183645
// possible to make it easy for LLVM to optimize away the thunk.
36193646
return ResultConvention::Indirect;
36203647
}
3648+
3649+
// Explicitly setting the ownership of the returned FRT if the C++ member
3650+
// method has either swift_attr("returns_retained") or
3651+
// ("returns_unretained") attribute.
3652+
if (auto resultConventionOpt =
3653+
getResultConventionWithAttributes(resultTL, TheDecl))
3654+
return *resultConventionOpt;
3655+
36213656
if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&
36223657
resultTL.getLoweredType().isForeignReferenceType()) {
36233658
return ResultConvention::Owned;
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#pragma once
2+
3+
// FRT or SWIFT_SHARED_REFERENCE type
4+
struct FRTStruct {
5+
} \
6+
__attribute__((swift_attr("import_reference"))) \
7+
__attribute__((swift_attr("retain:retainFRTStruct"))) \
8+
__attribute__((swift_attr("release:releaseFRTStruct")));
9+
10+
// Retain function for the FRT or SWIFT_SHARED_REFERENCE type FRTStruct
11+
void retainFRTStruct(FRTStruct * _Nonnull b) {
12+
}
13+
14+
// Release function for the FRT or SWIFT_SHARED_REFERENCE type FRTStruct
15+
void releaseFRTStruct(FRTStruct * _Nonnull b) {
16+
}
17+
18+
// Global/free C++ functions returning FRT without any attributes
19+
FRTStruct* _Nonnull global_function_returning_FRT();
20+
FRTStruct* _Nonnull global_function_returning_copy();
21+
FRTStruct* _Nonnull global_function_returning_create();
22+
FRTStruct* _Nonnull global_function_returning_init();
23+
FRTStruct* _Nonnull global_function_returning_clone();
24+
25+
// Global/free C++ functions returning FRT with attribute swift_attr("returns_retained") or SWIFT_RETURNS_RETAINED
26+
FRTStruct* _Nonnull global_function_returning_FRT_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
27+
FRTStruct* _Nonnull global_function_returning_copy_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
28+
FRTStruct* _Nonnull global_function_returning_create_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
29+
FRTStruct* _Nonnull global_function_returning_init_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
30+
FRTStruct* _Nonnull global_function_returning_clone_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
31+
32+
// Global/free C++ functions returning FRT with attribute swift_attr("returns_unretained") or SWIFT_RETURNS_UNRETAINED
33+
FRTStruct* _Nonnull global_function_returning_FRT_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
34+
FRTStruct* _Nonnull global_function_returning_copy_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
35+
FRTStruct* _Nonnull global_function_returning_create_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
36+
FRTStruct* _Nonnull global_function_returning_init_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
37+
FRTStruct* _Nonnull global_function_returning_clone_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
38+
39+
// Struct having static methods returning FRT without any attributes
40+
struct StructWithStaticMethodsReturningFRTWithoutAttributes {
41+
static FRTStruct* _Nonnull StaticMethodReturningFRT();
42+
static FRTStruct* _Nonnull StaticMethodReturningFRT_copy();
43+
static FRTStruct* _Nonnull StaticMethodReturningFRT_create();
44+
static FRTStruct* _Nonnull StaticMethodReturningFRT_init();
45+
static FRTStruct* _Nonnull StaticMethodReturningFRT_clone();
46+
};
47+
48+
// Struct having static methods returning FRT with attribute swift_attr("returns_retained") or SWIFT_RETURNS_RETAINED
49+
struct StructWithStaticMethodsReturningFRTWithAttributeReturnsRetained {
50+
static FRTStruct* _Nonnull StaticMethodReturningFRT() __attribute__((swift_attr("returns_retained")));
51+
static FRTStruct* _Nonnull StaticMethodReturningFRT_copy() __attribute__((swift_attr("returns_retained")));
52+
static FRTStruct* _Nonnull StaticMethodReturningFRT_create() __attribute__((swift_attr("returns_retained")));
53+
static FRTStruct* _Nonnull StaticMethodReturningFRT_init() __attribute__((swift_attr("returns_retained")));
54+
static FRTStruct* _Nonnull StaticMethodReturningFRT_clone() __attribute__((swift_attr("returns_retained")));
55+
};
56+
57+
// Struct having static methods returning FRT with attribute swift_attr("returns_unretained") or SWIFT_RETURNS_UNRETAINED
58+
struct StructWithStaticMethodsReturningFRTWithAttributeReturnsUnretained {
59+
static FRTStruct* _Nonnull StaticMethodReturningFRT() __attribute__((swift_attr("returns_unretained")));
60+
static FRTStruct* _Nonnull StaticMethodReturningFRT_copy() __attribute__((swift_attr("returns_unretained")));
61+
static FRTStruct* _Nonnull StaticMethodReturningFRT_create() __attribute__((swift_attr("returns_unretained")));
62+
static FRTStruct* _Nonnull StaticMethodReturningFRT_init() __attribute__((swift_attr("returns_unretained")));
63+
static FRTStruct* _Nonnull StaticMethodReturningFRT_clone() __attribute__((swift_attr("returns_unretained")));
64+
};
65+
66+
67+
// Global/free C++ functions returning FRT with both attributes swift_attr("returns_unretained") and swift_attr("returns_retained")
68+
FRTStruct* _Nonnull global_function_returning_FRT_with_both_attrs_returns_retained_returns_unretained() __attribute__((swift_attr("returns_retained"))) __attribute__((swift_attr("returns_unretained")));
69+
70+
// Struct having static method returning FRT with both attributes swift_attr("returns_unretained") and swift_attr("returns_retained")
71+
struct StructWithStaticMethodsReturningFRTWithBothAttributesReturnsRetainedAndReturnsUnretained {
72+
static FRTStruct* _Nonnull StaticMethodReturningFRT() __attribute__((swift_attr("returns_retained"))) __attribute__((swift_attr("returns_unretained")));
73+
};
74+
75+
struct NonFRTStruct {
76+
};
77+
78+
// Global/free C++ functions returning non-FRT
79+
NonFRTStruct* _Nonnull global_function_returning_non_FRT();
80+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
81+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
82+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_create();
83+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_copy();
84+
85+
86+
// Struct having static method returning non-FRT
87+
struct StructWithStaticMethodsReturningNonFRT {
88+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRT();
89+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRTWithAttrReturnsRetained() __attribute__((swift_attr("returns_retained")));
90+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRTWithAttrReturnsUnretained() __attribute__((swift_attr("returns_unretained")));
91+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRT_create();
92+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRT_copy();
93+
};
94+

test/Interop/Cxx/foreign-reference/Inputs/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ module DerivedFieldGetterReturnsOwnedFRT {
4949
requires cplusplus
5050
}
5151

52+
module FunctionsAndMethodsReturningFRT {
53+
header "cxx-functions-and-methods-returning-frt.h"
54+
requires cplusplus
55+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: rm -rf %t
2+
// RUN: not %target-swift-frontend -typecheck -I %S/Inputs %s -cxx-interoperability-mode=upcoming-swift 2>&1 | %FileCheck %s
3+
4+
import FunctionsAndMethodsReturningFRT
5+
6+
// Testing diagnostic for Global/free C++ functions returning FRT returning FRT with both attributes swift_attr("returns_unretained") and swift_attr("returns_retained")
7+
let frtLocalVar1 = global_function_returning_FRT_with_both_attrs_returns_retained_returns_unretained(); // CHECK: error: 'global_function_returning_FRT_with_both_attrs_returns_retained_returns_unretained' cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes
8+
9+
// Testing diagnostic for C++ Struct having static methods returning FRT returning FRT with both attributes swift_attr("returns_unretained") and swift_attr("returns_retained")
10+
let frtLocalVar2 = StructWithStaticMethodsReturningFRTWithBothAttributesReturnsRetainedAndReturnsUnretained.StaticMethodReturningFRT();; // CHECK: error: 'StaticMethodReturningFRT' cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes

0 commit comments

Comments
 (0)