Skip to content

Commit b8bc313

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 b8bc313

File tree

8 files changed

+412
-0
lines changed

8 files changed

+412
-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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3256,6 +3256,30 @@ namespace {
32563256
return property->getParsedAccessor(AccessorKind::Set);
32573257
}
32583258

3259+
// If a C++ decl is annotated with both swift_attr("returns_retained") and
3260+
// swift_attr("returns_unretained") then emit an error in the swift
3261+
// compiler. Note: this error is not emitted in the clang compiler because
3262+
// these attributes are used only for swift interop.
3263+
if (decl->hasAttrs()) {
3264+
bool returnsRetainedAttrIsPresent = false;
3265+
bool returnsUnretainedAttrIsPresent = false;
3266+
for (const auto *attr : decl->getAttrs()) {
3267+
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3268+
if (swiftAttr->getAttribute() == "returns_unretained") {
3269+
returnsUnretainedAttrIsPresent = true;
3270+
} else if (swiftAttr->getAttribute() == "returns_retained") {
3271+
returnsRetainedAttrIsPresent = true;
3272+
}
3273+
}
3274+
}
3275+
3276+
if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) {
3277+
HeaderLoc loc(decl->getLocation());
3278+
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
3279+
decl);
3280+
}
3281+
}
3282+
32593283
return importFunctionDecl(decl, importedName, correctSwiftName,
32603284
std::nullopt);
32613285
}

lib/ClangImporter/SwiftBridging/swift/bridging

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

161+
/// Specifies that the return type is passed as owned for C++ functions and
162+
/// methods returning types annotated as SWIFT_SHARED_REFERENCE
163+
#define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained")))
164+
/// Specifies that the return type is passed as unowned for C++ functions and
165+
/// methods returning types annotated as SWIFT_SHARED_REFERENCE
166+
#define SWIFT_RETURNS_UNRETAINED \
167+
__attribute__((swift_attr("returns_unretained")))
168+
161169
#else // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
162170

163171
// Empty defines for compilers that don't support `attribute(swift_attr)`.
@@ -172,6 +180,8 @@
172180
#define SWIFT_MUTATING
173181
#define SWIFT_UNCHECKED_SENDABLE
174182
#define SWIFT_NONCOPYABLE
183+
#define SWIFT_RETURNS_RETAINED
184+
#define SWIFT_RETURNS_UNRETAINED
175185

176186
#endif // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
177187

lib/SIL/IR/SILFunctionType.cpp

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

3426+
// Determines owned/unowned ResultConvention of the returned value based on
3427+
// returns_retained/returns_unretained attribute.
3428+
std::optional<ResultConvention>
3429+
getForeignReferenceTypeResultConventionWithAttributes(
3430+
const TypeLowering &tl, 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;
3443+
}
3444+
34263445
public:
34273446
CFunctionTypeConventions(const clang::FunctionType *type)
34283447
: Conventions(ConventionsKind::CFunctionType), FnType(type) {}
@@ -3540,6 +3559,13 @@ class CFunctionConventions : public CFunctionTypeConventions {
35403559
return ResultConvention::Indirect;
35413560
}
35423561

3562+
// Explicitly setting the ownership of the returned FRT if the C++
3563+
// global/free function has either swift_attr("returns_retained") or
3564+
// ("returns_unretained") attribute.
3565+
if (auto resultConventionOpt =
3566+
getForeignReferenceTypeResultConventionWithAttributes(tl, TheDecl))
3567+
return *resultConventionOpt;
3568+
35433569
if (isCFTypedef(tl, TheDecl->getReturnType())) {
35443570
// The CF attributes aren't represented in the type, so we need
35453571
// to check them here.
@@ -3618,6 +3644,14 @@ class CXXMethodConventions : public CFunctionTypeConventions {
36183644
// possible to make it easy for LLVM to optimize away the thunk.
36193645
return ResultConvention::Indirect;
36203646
}
3647+
3648+
// Explicitly setting the ownership of the returned FRT if the C++ member
3649+
// method has either swift_attr("returns_retained") or
3650+
// ("returns_unretained") attribute.
3651+
if (auto resultConventionOpt =
3652+
getForeignReferenceTypeResultConventionWithAttributes(resultTL, TheDecl))
3653+
return *resultConventionOpt;
3654+
36213655
if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&
36223656
resultTL.getLoweredType().isForeignReferenceType()) {
36233657
return ResultConvention::Owned;
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#pragma once
2+
3+
// FRT or SWIFT_SHARED_REFERENCE type
4+
struct FRTStruct {
5+
// Friend function declarations
6+
friend FRTStruct * returnInstanceOfFRTStruct(int v);
7+
friend FRTStruct * returnInstanceOfFRTStructWithAttrReturnsRetained(int v);
8+
friend FRTStruct * returnInstanceOfFRTStructWithAttrReturnsUnretained(int v);
9+
} \
10+
__attribute__((swift_attr("import_reference"))) \
11+
__attribute__((swift_attr("retain:retainFRTStruct"))) \
12+
__attribute__((swift_attr("release:releaseFRTStruct")));
13+
14+
// Retain function for the FRT or SWIFT_SHARED_REFERENCE type FRTStruct
15+
void retainFRTStruct(FRTStruct * _Nonnull b) {
16+
}
17+
18+
// Release function for the FRT or SWIFT_SHARED_REFERENCE type FRTStruct
19+
void releaseFRTStruct(FRTStruct * _Nonnull b) {
20+
}
21+
22+
// Friend function definition
23+
FRTStruct * returnInstanceOfFRTStruct() {
24+
return new FRTStruct;
25+
}
26+
27+
// Friend function definition
28+
FRTStruct * returnInstanceOfFRTStructWithAttrReturnsRetained() __attribute__((swift_attr("returns_retained"))) {
29+
return new FRTStruct;
30+
}
31+
32+
// Friend function definition
33+
FRTStruct * returnInstanceOfFRTStructWithAttrReturnsUnretained() __attribute__((swift_attr("returns_unretained"))) {
34+
return new FRTStruct;
35+
}
36+
37+
// Global/free C++ functions returning FRT without any attributes
38+
FRTStruct* _Nonnull global_function_returning_FRT();
39+
FRTStruct* _Nonnull global_function_returning_copy();
40+
FRTStruct* _Nonnull global_function_returning_create();
41+
FRTStruct* _Nonnull global_function_returning_init();
42+
FRTStruct* _Nonnull global_function_returning_clone();
43+
44+
// Global/free C++ functions returning FRT with attribute swift_attr("returns_retained") or SWIFT_RETURNS_RETAINED
45+
FRTStruct* _Nonnull global_function_returning_FRT_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
46+
FRTStruct* _Nonnull global_function_returning_copy_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
47+
FRTStruct* _Nonnull global_function_returning_create_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
48+
FRTStruct* _Nonnull global_function_returning_init_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
49+
FRTStruct* _Nonnull global_function_returning_clone_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
50+
51+
// Global/free C++ functions returning FRT with attribute swift_attr("returns_unretained") or SWIFT_RETURNS_UNRETAINED
52+
FRTStruct* _Nonnull global_function_returning_FRT_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
53+
FRTStruct* _Nonnull global_function_returning_copy_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
54+
FRTStruct* _Nonnull global_function_returning_create_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
55+
FRTStruct* _Nonnull global_function_returning_init_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
56+
FRTStruct* _Nonnull global_function_returning_clone_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
57+
58+
// Static Global/free functions returning FRT
59+
FRTStruct* _Nonnull global_static_function_returning_FRT();
60+
FRTStruct* _Nonnull global_static_function_returning_FRT_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
61+
FRTStruct* _Nonnull global_static_function_returning_FRT_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
62+
FRTStruct* _Nonnull global_static_function_returning_copy();
63+
FRTStruct* _Nonnull global_static_function_returning_create();
64+
FRTStruct* _Nonnull global_static_function_returning_copy_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
65+
FRTStruct* _Nonnull global_static_function_returning_copy_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
66+
67+
// Global/free functions returning FRT without _Nonnull
68+
FRTStruct* global_function_returning_FRT_wihout_Nonnull();
69+
FRTStruct* global_function_returning_FRT_with_attr_returns_retained_wihout_Nonnull() __attribute__((swift_attr("returns_retained")));
70+
FRTStruct* global_function_returning_FRT_with_attr_returns_unretained_wihout_Nonnull() __attribute__((swift_attr("returns_unretained")));
71+
FRTStruct* global_function_returning_copy_wihout_Nonnull();
72+
FRTStruct* global_function_returning_create_wihout_Nonnull();
73+
FRTStruct* global_function_returning_copy_with_attr_returns_retained_wihout_Nonnull() __attribute__((swift_attr("returns_retained")));
74+
FRTStruct* global_function_returning_copy_with_attr_returns_unretained_wihout_Nonnull() __attribute__((swift_attr("returns_unretained")));
75+
76+
// Struct having static methods returning FRT without any attributes
77+
struct StructWithStaticMethodsReturningFRTWithoutAttributes {
78+
static FRTStruct* _Nonnull StaticMethodReturningFRT();
79+
static FRTStruct* _Nonnull StaticMethodReturningFRT_copy();
80+
static FRTStruct* _Nonnull StaticMethodReturningFRT_create();
81+
static FRTStruct* _Nonnull StaticMethodReturningFRT_init();
82+
static FRTStruct* _Nonnull StaticMethodReturningFRT_clone();
83+
};
84+
85+
// Struct having static methods returning FRT with attribute swift_attr("returns_retained") or SWIFT_RETURNS_RETAINED
86+
struct StructWithStaticMethodsReturningFRTWithAttributeReturnsRetained {
87+
static FRTStruct* _Nonnull StaticMethodReturningFRT() __attribute__((swift_attr("returns_retained")));
88+
static FRTStruct* _Nonnull StaticMethodReturningFRT_copy() __attribute__((swift_attr("returns_retained")));
89+
static FRTStruct* _Nonnull StaticMethodReturningFRT_create() __attribute__((swift_attr("returns_retained")));
90+
static FRTStruct* _Nonnull StaticMethodReturningFRT_init() __attribute__((swift_attr("returns_retained")));
91+
static FRTStruct* _Nonnull StaticMethodReturningFRT_clone() __attribute__((swift_attr("returns_retained")));
92+
};
93+
94+
// Struct having static methods returning FRT with attribute swift_attr("returns_unretained") or SWIFT_RETURNS_UNRETAINED
95+
struct StructWithStaticMethodsReturningFRTWithAttributeReturnsUnretained {
96+
static FRTStruct* _Nonnull StaticMethodReturningFRT() __attribute__((swift_attr("returns_unretained")));
97+
static FRTStruct* _Nonnull StaticMethodReturningFRT_copy() __attribute__((swift_attr("returns_unretained")));
98+
static FRTStruct* _Nonnull StaticMethodReturningFRT_create() __attribute__((swift_attr("returns_unretained")));
99+
static FRTStruct* _Nonnull StaticMethodReturningFRT_init() __attribute__((swift_attr("returns_unretained")));
100+
static FRTStruct* _Nonnull StaticMethodReturningFRT_clone() __attribute__((swift_attr("returns_unretained")));
101+
};
102+
103+
// Global/free C++ functions returning FRT with both attributes swift_attr("returns_unretained") and swift_attr("returns_retained")
104+
FRTStruct* _Nonnull global_function_returning_FRT_with_both_attrs_returns_retained_returns_unretained() __attribute__((swift_attr("returns_retained"))) __attribute__((swift_attr("returns_unretained")));
105+
106+
// Struct having static method returning FRT with both attributes swift_attr("returns_unretained") and swift_attr("returns_retained")
107+
struct StructWithStaticMethodsReturningFRTWithBothAttributesReturnsRetainedAndReturnsUnretained {
108+
static FRTStruct* _Nonnull StaticMethodReturningFRT() __attribute__((swift_attr("returns_retained"))) __attribute__((swift_attr("returns_unretained")));
109+
};
110+
111+
struct NonFRTStruct {
112+
};
113+
114+
// Global/free C++ functions returning non-FRT
115+
NonFRTStruct* _Nonnull global_function_returning_non_FRT();
116+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
117+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
118+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_create();
119+
NonFRTStruct* _Nonnull global_function_returning_non_FRT_copy();
120+
121+
122+
// Struct having static method returning non-FRT
123+
struct StructWithStaticMethodsReturningNonFRT {
124+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRT();
125+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRTWithAttrReturnsRetained() __attribute__((swift_attr("returns_retained")));
126+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRTWithAttrReturnsUnretained() __attribute__((swift_attr("returns_unretained")));
127+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRT_create();
128+
static NonFRTStruct* _Nonnull StaticMethodReturningNonFRT_copy();
129+
};

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+
let frtLocalVar1 = global_function_returning_FRT_with_both_attrs_returns_retained_returns_unretained()
7+
// 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+
let frtLocalVar2 = StructWithStaticMethodsReturningFRTWithBothAttributesReturnsRetainedAndReturnsUnretained.StaticMethodReturningFRT()
10+
// CHECK: error: 'StaticMethodReturningFRT' cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes

0 commit comments

Comments
 (0)