Skip to content

Commit 493561a

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
rdar://135306908
1 parent a4a157c commit 493561a

File tree

8 files changed

+484
-0
lines changed

8 files changed

+484
-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
@@ -3262,6 +3262,30 @@ namespace {
32623262
return property->getParsedAccessor(AccessorKind::Set);
32633263
}
32643264

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

lib/ClangImporter/SwiftBridging/swift/bridging

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@
164164
#define SWIFT_NONESCAPABLE \
165165
__attribute__((swift_attr("~Escapable")))
166166

167+
/// Specifies that the return value is passed as owned for C++ functions and
168+
/// methods returning types annotated as SWIFT_SHARED_REFERENCE
169+
#define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained")))
170+
/// Specifies that the return value is passed as unowned for C++ functions and
171+
/// methods returning types annotated as SWIFT_SHARED_REFERENCE
172+
#define SWIFT_RETURNS_UNRETAINED \
173+
__attribute__((swift_attr("returns_unretained")))
174+
167175
#else // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
168176

169177
// Empty defines for compilers that don't support `attribute(swift_attr)`.
@@ -179,6 +187,8 @@
179187
#define SWIFT_UNCHECKED_SENDABLE
180188
#define SWIFT_NONCOPYABLE
181189
#define SWIFT_NONESCAPABLE
190+
#define SWIFT_RETURNS_RETAINED
191+
#define SWIFT_RETURNS_UNRETAINED
182192

183193
#endif // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
184194

lib/SIL/IR/SILFunctionType.cpp

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

3360+
// Determines owned/unowned ResultConvention of the returned value based on
3361+
// returns_retained/returns_unretained attribute.
3362+
std::optional<ResultConvention>
3363+
getForeignReferenceTypeResultConventionWithAttributes(
3364+
const TypeLowering &tl, const clang::FunctionDecl *decl) const {
3365+
if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) {
3366+
for (const auto *attr : decl->getAttrs()) {
3367+
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3368+
if (swiftAttr->getAttribute() == "returns_unretained") {
3369+
return ResultConvention::Unowned;
3370+
} else if (swiftAttr->getAttribute() == "returns_retained") {
3371+
return ResultConvention::Owned;
3372+
}
3373+
}
3374+
}
3375+
}
3376+
return std::nullopt;
3377+
}
3378+
33603379
public:
33613380
CFunctionTypeConventions(const clang::FunctionType *type)
33623381
: Conventions(ConventionsKind::CFunctionType), FnType(type) {}
@@ -3474,6 +3493,13 @@ class CFunctionConventions : public CFunctionTypeConventions {
34743493
return ResultConvention::Indirect;
34753494
}
34763495

3496+
// Explicitly setting the ownership of the returned FRT if the C++
3497+
// global/free function has either swift_attr("returns_retained") or
3498+
// ("returns_unretained") attribute.
3499+
if (auto resultConventionOpt =
3500+
getForeignReferenceTypeResultConventionWithAttributes(tl, TheDecl))
3501+
return *resultConventionOpt;
3502+
34773503
if (isCFTypedef(tl, TheDecl->getReturnType())) {
34783504
// The CF attributes aren't represented in the type, so we need
34793505
// to check them here.
@@ -3552,6 +3578,14 @@ class CXXMethodConventions : public CFunctionTypeConventions {
35523578
// possible to make it easy for LLVM to optimize away the thunk.
35533579
return ResultConvention::Indirect;
35543580
}
3581+
3582+
// Explicitly setting the ownership of the returned FRT if the C++ member
3583+
// method has either swift_attr("returns_retained") or
3584+
// ("returns_unretained") attribute.
3585+
if (auto resultConventionOpt =
3586+
getForeignReferenceTypeResultConventionWithAttributes(resultTL, TheDecl))
3587+
return *resultConventionOpt;
3588+
35553589
if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&
35563590
resultTL.getLoweredType().isForeignReferenceType()) {
35573591
return ResultConvention::Owned;
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
static FRTStruct* _Nonnull global_static_function_returning_FRT();
60+
static FRTStruct* _Nonnull global_static_function_returning_FRT_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
61+
static FRTStruct* _Nonnull global_static_function_returning_FRT_with_attr_returns_unretained() __attribute__((swift_attr("returns_unretained")));
62+
static FRTStruct* _Nonnull global_static_function_returning_copy();
63+
static FRTStruct* _Nonnull global_static_function_returning_create();
64+
static FRTStruct* _Nonnull global_static_function_returning_copy_with_attr_returns_retained() __attribute__((swift_attr("returns_retained")));
65+
static 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+
};
130+
131+
// Tests for templated functions
132+
template <typename T>
133+
FRTStruct * _Nonnull global_templated_function_returning_FRT(T a);
134+
135+
template <typename T>
136+
FRTStruct * _Nonnull global_templated_function_returning_FRT_copy(T a);
137+
138+
template <typename T>
139+
FRTStruct * _Nonnull global_templated_function_returning_FRT_create(T a);
140+
141+
template <typename T>
142+
FRTStruct * _Nonnull global_templated_function_returning_FRT_init(T a);
143+
144+
template <typename T>
145+
FRTStruct * _Nonnull global_templated_function_returning_FRT_clone(T a);
146+
147+
template <typename T>
148+
FRTStruct * _Nonnull global_templated_function_returning_FRT_with_attr_returns_retained(T a) __attribute__((swift_attr("returns_retained")));
149+
150+
template <typename T>
151+
FRTStruct * _Nonnull global_templated_function_returning_FRT_copy_with_attr_returns_retained(T a) __attribute__((swift_attr("returns_retained")));
152+
153+
template <typename T>
154+
FRTStruct * _Nonnull global_templated_function_returning_FRT_create_with_attr_returns_retained(T a) __attribute__((swift_attr("returns_retained")));
155+
156+
template <typename T>
157+
FRTStruct * _Nonnull global_templated_function_returning_FRT_with_attr_returns_unretained(T a) __attribute__((swift_attr("returns_unretained")));
158+
159+
template <typename T>
160+
FRTStruct * _Nonnull global_templated_function_returning_FRT_copy_with_attr_returns_unretained(T a) __attribute__((swift_attr("returns_unretained")));
161+
162+
template <typename T>
163+
FRTStruct * _Nonnull global_templated_function_returning_FRT_create_with_attr_returns_unretained(T a) __attribute__((swift_attr("returns_unretained")));

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)