Skip to content

Commit 70d72fe

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 70d72fe

File tree

8 files changed

+276
-0
lines changed

8 files changed

+276
-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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3256,6 +3256,15 @@ 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 (hasReturnsRetainedAttr(decl) && hasReturnsUnretainedAttr(decl)) {
3262+
// llvm::errs() << "ERROR: can't have both ways\n\n\n";
3263+
HeaderLoc loc(decl->getLocation());
3264+
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
3265+
decl);
3266+
}
3267+
32593268
return importFunctionDecl(decl, importedName, correctSwiftName,
32603269
std::nullopt);
32613270
}
@@ -3750,6 +3759,27 @@ namespace {
37503759
});
37513760
}
37523761

3762+
// Helper function to check if the FunctionDecl has
3763+
// swift_attr("returns_retained") attribute
3764+
static bool hasReturnsRetainedAttr(const clang::FunctionDecl *decl) {
3765+
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
3766+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
3767+
return swiftAttr->getAttribute() == "returns_retained";
3768+
return false;
3769+
});
3770+
}
3771+
3772+
// Helper function to check if the FunctionDecl has
3773+
// swift_attr("returns_unretained") attribute
3774+
static bool hasReturnsUnretainedAttr(const clang::FunctionDecl *decl) {
3775+
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
3776+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
3777+
return swiftAttr->getAttribute() == "returns_unretained";
3778+
return false;
3779+
});
3780+
;
3781+
}
3782+
37533783
Decl *VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) {
37543784
// The static `operator ()` introduced in C++ 23 is still callable as an
37553785
// instance operator in C++, and we want to preserve the ability to call

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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3423,6 +3423,27 @@ class CFunctionTypeConventions : public Conventions {
34233423
const clang::FunctionType *type)
34243424
: Conventions(kind), FnType(type) {}
34253425

3426+
// Helper function to check if the FunctionDecl has
3427+
// swift_attr("returns_retained") attribute
3428+
static bool hasReturnsRetainedAttr(const clang::FunctionDecl *decl) {
3429+
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
3430+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
3431+
return swiftAttr->getAttribute() == "returns_retained";
3432+
return false;
3433+
});
3434+
}
3435+
3436+
// Helper function to check if the FunctionDecl has
3437+
// swift_attr("returns_unretained") attribute
3438+
static bool hasReturnsUnretainedAttr(const clang::FunctionDecl *decl) {
3439+
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
3440+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
3441+
return swiftAttr->getAttribute() == "returns_unretained";
3442+
return false;
3443+
});
3444+
;
3445+
}
3446+
34263447
public:
34273448
CFunctionTypeConventions(const clang::FunctionType *type)
34283449
: Conventions(ConventionsKind::CFunctionType), FnType(type) {}
@@ -3540,6 +3561,17 @@ class CFunctionConventions : public CFunctionTypeConventions {
35403561
return ResultConvention::Indirect;
35413562
}
35423563

3564+
// Explicitly setting the ownership of the returned FRT if the C++
3565+
// global/free function has either swift_attr("returns_retained") or
3566+
// ("returns_unretained") attribute.
3567+
if (tl.getLoweredType().isForeignReferenceType()) {
3568+
if (hasReturnsUnretainedAttr(TheDecl)) {
3569+
return ResultConvention::Unowned;
3570+
} else if (hasReturnsRetainedAttr(TheDecl)) {
3571+
return ResultConvention::Owned;
3572+
}
3573+
}
3574+
35433575
if (isCFTypedef(tl, TheDecl->getReturnType())) {
35443576
// The CF attributes aren't represented in the type, so we need
35453577
// to check them here.
@@ -3618,6 +3650,18 @@ class CXXMethodConventions : public CFunctionTypeConventions {
36183650
// possible to make it easy for LLVM to optimize away the thunk.
36193651
return ResultConvention::Indirect;
36203652
}
3653+
3654+
// Explicitly setting the ownership of the returned FRT if the C++ member
3655+
// method has either swift_attr("returns_retained") or
3656+
// ("returns_unretained") attribute.
3657+
if (resultTL.getLoweredType().isForeignReferenceType()) {
3658+
if (hasReturnsUnretainedAttr(TheDecl)) {
3659+
return ResultConvention::Unowned;
3660+
} else if (hasReturnsRetainedAttr(TheDecl)) {
3661+
return ResultConvention::Owned;
3662+
}
3663+
}
3664+
36213665
if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&
36223666
resultTL.getLoweredType().isForeignReferenceType()) {
36233667
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)