Skip to content

Commit 3887ed0

Browse files
committed
[cxx-interop] Allow retain/release operations to return an unsigned integer
Release/retain functions for C++ foreign reference types might return a reference count as an integer value. Swift previously emitted an error for such functions, saying that the retain/release functions need to return void or a reference to the value. rdar://157853183
1 parent 0f963d7 commit 3887ed0

File tree

5 files changed

+47
-9
lines changed

5 files changed

+47
-9
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,11 +261,11 @@ ERROR(foreign_reference_types_invalid_retain_release, none,
261261

262262
ERROR(foreign_reference_types_retain_non_void_or_self_return_type, none,
263263
"specified retain function '%0' is invalid; "
264-
"retain function must have 'void' or parameter return type",
264+
"retain function must either return have 'void', the reference count as an integer, or the parameter type",
265265
(StringRef))
266266
ERROR(foreign_reference_types_release_non_void_return_type, none,
267267
"specified release function '%0' is invalid; "
268-
"release function must have 'void' return type",
268+
"release function must either return 'void' or the reference count as an integer",
269269
(StringRef))
270270
ERROR(foreign_reference_types_retain_release_not_a_function_decl, none,
271271
"specified %select{retain|release}0 function '%1' is not a function",

lib/ClangImporter/ImportDecl.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2739,7 +2739,7 @@ namespace {
27392739

27402740
enum class RetainReleaseOperationKind {
27412741
notAfunction,
2742-
doesntReturnVoidOrSelf,
2742+
invalidReturnType,
27432743
invalidParameters,
27442744
valid
27452745
};
@@ -2767,10 +2767,20 @@ namespace {
27672767
// The return type should be void (for release functions), or void
27682768
// or the parameter type (for retain functions).
27692769
auto resultInterfaceType = operationFn->getResultInterfaceType();
2770-
if (!resultInterfaceType->isVoid()) {
2770+
if (!resultInterfaceType->isVoid() &&
2771+
!resultInterfaceType->isUInt() &&
2772+
!resultInterfaceType->isUInt8() &&
2773+
!resultInterfaceType->isUInt16() &&
2774+
!resultInterfaceType->isUInt32() &&
2775+
!resultInterfaceType->isUInt64() &&
2776+
!resultInterfaceType->isInt() &&
2777+
!resultInterfaceType->isInt8() &&
2778+
!resultInterfaceType->isInt16() &&
2779+
!resultInterfaceType->isInt32() &&
2780+
!resultInterfaceType->isInt64()) {
27712781
if (operationKind == CustomRefCountingOperationKind::release ||
27722782
!resultInterfaceType->lookThroughSingleOptionalType()->isEqual(paramType))
2773-
return RetainReleaseOperationKind::doesntReturnVoidOrSelf;
2783+
return RetainReleaseOperationKind::invalidReturnType;
27742784
}
27752785

27762786
// The parameter of the retain/release function should be pointer to the
@@ -2835,7 +2845,7 @@ namespace {
28352845
diag::foreign_reference_types_retain_release_not_a_function_decl,
28362846
false, retainOperation.name);
28372847
break;
2838-
case RetainReleaseOperationKind::doesntReturnVoidOrSelf:
2848+
case RetainReleaseOperationKind::invalidReturnType:
28392849
Impl.diagnose(
28402850
loc,
28412851
diag::foreign_reference_types_retain_non_void_or_self_return_type,
@@ -2900,7 +2910,7 @@ namespace {
29002910
diag::foreign_reference_types_retain_release_not_a_function_decl,
29012911
true, releaseOperation.name);
29022912
break;
2903-
case RetainReleaseOperationKind::doesntReturnVoidOrSelf:
2913+
case RetainReleaseOperationKind::invalidReturnType:
29042914
Impl.diagnose(
29052915
loc,
29062916
diag::foreign_reference_types_release_non_void_return_type,

test/Interop/Cxx/foreign-reference/Inputs/reference-counted.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ GlobalCountNullableInit {
6868

6969
inline void GCRetainNullableInit(GlobalCountNullableInit *x) { globalCount++; }
7070
inline void GCReleaseNullableInit(GlobalCountNullableInit *x) { globalCount--; }
71+
72+
struct __attribute__((swift_attr("import_as_ref")))
73+
__attribute__((swift_attr("retain:RCRetain")))
74+
__attribute__((swift_attr("release:RCRelease"))) HasOpsReturningRefCount final {
75+
int refCount = 0;
76+
77+
static HasOpsReturningRefCount *create() {
78+
return new (malloc(sizeof(HasOpsReturningRefCount)))
79+
HasOpsReturningRefCount();
80+
}
81+
};
82+
83+
inline unsigned RCRetain(HasOpsReturningRefCount *x) { return ++x->refCount; }
84+
inline unsigned RCRelease(HasOpsReturningRefCount *x) { return --x->refCount; }
85+
7186
#endif
7287

7388
typedef struct __attribute__((swift_attr("import_as_ref")))

test/Interop/Cxx/foreign-reference/invalid-retain-operation-errors.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct
2525
__attribute__((swift_attr("release:badRelease")))
2626
BadRetainRelease {};
2727

28-
int badRetain(BadRetainRelease *v);
28+
float badRetain(BadRetainRelease *v);
2929
void badRelease(BadRetainRelease *v, int i);
3030

3131
struct
@@ -235,7 +235,7 @@ public func test(x: NonExistent) { }
235235
@available(macOS 13.3, *)
236236
public func test(x: NoRetainRelease) { }
237237

238-
// CHECK: error: specified retain function 'badRetain' is invalid; retain function must have 'void' or parameter return type
238+
// CHECK: error: specified retain function 'badRetain' is invalid; retain function must either return have 'void', the reference count as an integer, or the parameter type
239239
// CHECK: error: specified release function 'badRelease' is invalid; release function must have exactly one argument of type 'BadRetainRelease'
240240
@available(macOS 13.3, *)
241241
public func test(x: BadRetainRelease) { }

test/Interop/Cxx/foreign-reference/reference-counted-irgen.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ public func getLocalCount() -> NS.LocalCount {
1717
// CHECK-NEXT: }
1818

1919

20+
public func useRetainReleaseOpsReturningRefCount() -> HasOpsReturningRefCount {
21+
let result = HasOpsReturningRefCount.create()
22+
return result
23+
}
24+
25+
// CHECK: define {{.*}}swiftcc ptr @"$s4main36useRetainReleaseOpsReturningRefCountSo03HasefgH0VyF"()
26+
// CHECK-NEXT: entry:
27+
// CHECK: %0 = call ptr @{{_ZN23HasOpsReturningRefCount6createEv|"\?create\@HasOpsReturningRefCount\@\@SAPEAU1\@XZ"}}()
28+
// CHECK: %1 = call i32 @{{_Z8RCRetainP23HasOpsReturningRefCount|"\?RCRetain\@\@YAIPEAUHasOpsReturningRefCount\@\@\@Z"}}(ptr %0)
29+
// CHECK: ret ptr %0
30+
// CHECK-NEXT: }
31+
32+
2033
public func get42() -> Int32 {
2134
let result = NS.LocalCount.create()
2235
return result.returns42()

0 commit comments

Comments
 (0)