Skip to content

Commit 0bc94e9

Browse files
committed
[cxx-interop] Introduce type-level annotations to specify default ownership convention for C++ foreign reference return values
1 parent 54e4400 commit 0bc94e9

File tree

6 files changed

+194
-9
lines changed

6 files changed

+194
-9
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3664,6 +3664,25 @@ namespace {
36643664
unannotatedAPIWarningNeeded = true;
36653665
}
36663666

3667+
if (const auto *returnPtrTy = retType->getAs<clang::PointerType>()) {
3668+
clang::QualType returnPointeeType = returnPtrTy->getPointeeType();
3669+
if (const auto *returnRecordType =
3670+
returnPointeeType->getAs<clang::RecordType>()) {
3671+
clang::RecordDecl *returnRecordDecl = returnRecordType->getDecl();
3672+
for (const auto *attr : returnRecordDecl->getAttrs()) {
3673+
if (const auto *swiftAttr =
3674+
dyn_cast<clang::SwiftAttrAttr>(attr)) {
3675+
if (swiftAttr->getAttribute() ==
3676+
"returns_retained_by_default" ||
3677+
swiftAttr->getAttribute() ==
3678+
"returns_unretained_by_default") {
3679+
unannotatedAPIWarningNeeded = false;
3680+
}
3681+
}
3682+
}
3683+
}
3684+
}
3685+
36673686
if (unannotatedAPIWarningNeeded) {
36683687
HeaderLoc loc(decl->getLocation());
36693688
Impl.diagnose(loc, diag::no_returns_retained_returns_unretained,

lib/ClangImporter/SwiftBridging/swift/bridging

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,20 @@
191191
#define SWIFT_RETURNS_UNRETAINED \
192192
__attribute__((swift_attr("returns_unretained")))
193193

194+
/// Specifies that a C++ API returning this C++ foreign reference type is
195+
/// assumed to return an owned (+1) reference by default, unless explicitly
196+
/// annotated with SWIFT_RETURNS_UNRETAINED. This annotation is only valid on
197+
/// types that are already annotated with SWIFT_SHARED_REFERENCE.
198+
#define SWIFT_RETURNS_RETAINED_BY_DEFAULT \
199+
__attribute__((swift_attr("returns_retained_by_default")))
200+
201+
/// Specifies that a C++ API returning this C++ foreign reference type is
202+
/// assumed to return an unowned (+0) reference by default, unless explicitly
203+
/// annotated with SWIFT_RETURNS_RETAINED. This annotation is only valid on
204+
/// types that are already annotated with SWIFT_SHARED_REFERENCE.
205+
#define SWIFT_RETURNS_UNRETAINED_BY_DEFAULT \
206+
__attribute__((swift_attr("returns_unretained_by_default")))
207+
194208
/// Specifies that the non-public members of a C++ class, struct, or union can
195209
/// be accessed from extensions of that type, in the given file ID.
196210
///

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,18 +1345,43 @@ class Conventions {
13451345
llvm_unreachable("unhandled ownership");
13461346
}
13471347

1348-
// Determines owned/unowned ResultConvention of the returned value based on
1349-
// returns_retained/returns_unretained attribute.
1348+
// Determines the ownership ResultConvention (owned/unowned) of the return
1349+
// value using the SWIFT_RETURNS_(UN)RETAINED annotation on the C++ API; if
1350+
// not explicitly annotated, falls back to the
1351+
// SWIFT_RETURNS_(UN)RETAINED_BY_DEFAULT annotation on the C++
1352+
// SWIFT_SHARED_REFERENCE type.
13501353
std::optional<ResultConvention>
13511354
getCxxRefConventionWithAttrs(const TypeLowering &tl,
13521355
const clang::Decl *decl) const {
1353-
if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) {
1354-
for (const auto *attr : decl->getAttrs()) {
1355-
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
1356-
if (swiftAttr->getAttribute() == "returns_unretained") {
1357-
return ResultConvention::Unowned;
1358-
} else if (swiftAttr->getAttribute() == "returns_retained") {
1359-
return ResultConvention::Owned;
1356+
if (tl.getLoweredType().isForeignReferenceType()) {
1357+
if (decl->hasAttrs()) {
1358+
for (const auto *attr : decl->getAttrs()) {
1359+
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
1360+
if (swiftAttr->getAttribute() == "returns_unretained") {
1361+
return ResultConvention::Unowned;
1362+
} else if (swiftAttr->getAttribute() == "returns_retained") {
1363+
return ResultConvention::Owned;
1364+
}
1365+
}
1366+
}
1367+
}
1368+
if (auto *classDecl = tl.getLoweredType()
1369+
.getASTType()
1370+
.getPointer()
1371+
->lookThroughAllOptionalTypes()
1372+
->getClassOrBoundGenericClass()) {
1373+
if (auto clangRecordDecl =
1374+
dyn_cast<clang::RecordDecl>(classDecl->getClangDecl())) {
1375+
for (const auto *attr : clangRecordDecl->getAttrs()) {
1376+
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
1377+
if (swiftAttr->getAttribute() ==
1378+
"returns_unretained_by_default") {
1379+
return ResultConvention::Unowned;
1380+
} else if (swiftAttr->getAttribute() ==
1381+
"returns_retained_by_default") {
1382+
return ResultConvention::Owned;
1383+
}
1384+
}
13601385
}
13611386
}
13621387
}

test/Interop/Cxx/foreign-reference/Inputs/cxx-functions-and-methods-returning-frt.h

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include "visibility.h"
23
#include <functional>
34

45
// FRT or SWIFT_SHARED_REFERENCE type
@@ -326,3 +327,107 @@ struct Derived : Base {
326327
FRTStruct *_Nonnull VirtualMethodReturningFRTUnowned() override
327328
__attribute__((swift_attr("returns_unretained")));
328329
};
330+
331+
SWIFT_BEGIN_NULLABILITY_ANNOTATIONS
332+
333+
namespace DefaultOwnershipConventionOnCXXForegnRefType {
334+
struct __attribute__((swift_attr("import_reference")))
335+
__attribute__((swift_attr("retain:defRetain1")))
336+
__attribute__((swift_attr("release:defRelease1"))) __attribute__((
337+
swift_attr("returns_retained_by_default"))) RefTypeDefaultRetained {};
338+
339+
RefTypeDefaultRetained *returnRefTypeDefaultRetained() {
340+
return new RefTypeDefaultRetained();
341+
}
342+
343+
struct __attribute__((swift_attr("import_reference")))
344+
__attribute__((swift_attr("retain:defRetain2")))
345+
__attribute__((swift_attr("release:defRelease2"))) __attribute__((
346+
swift_attr("returns_unretained_by_default"))) RefTypeDefaultUnretained {};
347+
348+
RefTypeDefaultUnretained *returnRefTypeDefaultUnretained() {
349+
return new RefTypeDefaultUnretained();
350+
}
351+
} // namespace DefaultOwnershipConventionOnCXXForegnRefType
352+
353+
void defRetain1(
354+
DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultRetained *v) {};
355+
void defRelease1(
356+
DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultRetained *v) {};
357+
358+
void defRetain2(
359+
DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultUnretained *v) {
360+
};
361+
void defRelease2(
362+
DefaultOwnershipConventionOnCXXForegnRefType::RefTypeDefaultUnretained *v) {
363+
};
364+
365+
namespace FunctionAnnotationHasPrecedence {
366+
struct __attribute__((swift_attr("import_reference")))
367+
__attribute__((swift_attr("retain:defaultRetain1")))
368+
__attribute__((swift_attr("release:defaultRelease1"))) __attribute__((
369+
swift_attr("returns_unretained_by_default"))) RefTypeDefaultUnRetained {};
370+
371+
RefTypeDefaultUnRetained *returnRefTypeDefaultUnRetained() {
372+
return new RefTypeDefaultUnRetained();
373+
}
374+
RefTypeDefaultUnRetained *returnRefTypeDefaultUnRetainedAnnotatedRetained()
375+
__attribute__((swift_attr("returns_retained"))) {
376+
return new RefTypeDefaultUnRetained();
377+
}
378+
379+
struct __attribute__((swift_attr("import_reference")))
380+
__attribute__((swift_attr("retain:defaultRetain2")))
381+
__attribute__((swift_attr("release:defaultRelease2"))) __attribute__((
382+
swift_attr("returns_retained_by_default"))) RefTypeDefaultRetained {};
383+
384+
RefTypeDefaultRetained *returnRefTypeDefaultRetained() {
385+
return new RefTypeDefaultRetained();
386+
}
387+
RefTypeDefaultRetained *returnRefTypeDefaultRetainedAnnotatedUnRetained()
388+
__attribute__((swift_attr("returns_unretained"))) {
389+
return new RefTypeDefaultRetained();
390+
}
391+
392+
} // namespace FunctionAnnotationHasPrecedence
393+
394+
void defaultRetain1(
395+
FunctionAnnotationHasPrecedence::RefTypeDefaultUnRetained *v) {};
396+
void defaultRelease1(
397+
FunctionAnnotationHasPrecedence::RefTypeDefaultUnRetained *v) {};
398+
399+
void defaultRetain2(
400+
FunctionAnnotationHasPrecedence::RefTypeDefaultRetained *v) {};
401+
void defaultRelease2(
402+
FunctionAnnotationHasPrecedence::RefTypeDefaultRetained *v) {};
403+
404+
namespace DefaultOwnershipSuppressUnannotatedAPIWarning {
405+
406+
struct __attribute__((swift_attr("import_reference")))
407+
__attribute__((swift_attr("retain:rretain")))
408+
__attribute__((swift_attr("release:rrelease"))) RefType {};
409+
410+
RefType *returnRefType() { return new RefType(); } // expected-warning {{'returnRefType' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}}
411+
412+
struct __attribute__((swift_attr("import_reference")))
413+
__attribute__((swift_attr("retain:dretain")))
414+
__attribute__((swift_attr("release:drelease"))) __attribute__((
415+
swift_attr("returns_retained_by_default"))) RefTypeDefaultRetained {};
416+
417+
RefTypeDefaultRetained *returnRefTypeDefaultRetained() {
418+
return new RefTypeDefaultRetained();
419+
}
420+
421+
} // namespace DefaultOwnershipSuppressUnannotatedAPIWarning
422+
423+
void rretain(DefaultOwnershipSuppressUnannotatedAPIWarning::RefType *v) {};
424+
void rrelease(DefaultOwnershipSuppressUnannotatedAPIWarning::RefType *v) {};
425+
426+
void dretain(
427+
DefaultOwnershipSuppressUnannotatedAPIWarning::RefTypeDefaultRetained *v) {
428+
};
429+
void drelease(
430+
DefaultOwnershipSuppressUnannotatedAPIWarning::RefTypeDefaultRetained *v) {
431+
};
432+
433+
SWIFT_END_NULLABILITY_ANNOTATIONS

test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes-error.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ let f = FunctionVoidToFRTStruct()
3232
let frt = f()
3333
let nonFrt = NonFRTStruct()
3434
let nonFrtLocalVar1 = global_function_returning_templated_retrun_frt_owned(nonFrt)
35+
let _ = DefaultOwnershipSuppressUnannotatedAPIWarning.returnRefType()
36+
let _ = DefaultOwnershipSuppressUnannotatedAPIWarning.returnRefTypeDefaultRetained()

test/Interop/Cxx/foreign-reference/frt-retained-unretained-attributes.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,23 @@ func testVirtualMethods(base: Base, derived: Derived) {
247247
var frt4 = mutableDerived.VirtualMethodReturningFRTOwned()
248248
// CHECK: function_ref @{{.*}}VirtualMethodReturningFRTOwned{{.*}} : $@convention(cxx_method) (@inout Derived) -> @owned FRTStruct
249249
}
250+
251+
func testDefaultOwnershipAnnotation() {
252+
let _ = DefaultOwnershipConventionOnCXXForegnRefType.returnRefTypeDefaultRetained()
253+
// CHECK: function_ref {{.*}}returnRefTypeDefaultRetained{{.*}} : $@convention(c) () -> @owned DefaultOwnershipConventionOnCXXForegnRefType.RefTypeDefaultRetained
254+
255+
let _ = DefaultOwnershipConventionOnCXXForegnRefType.returnRefTypeDefaultUnretained()
256+
// CHECK: function_ref {{.*}}returnRefTypeDefaultUnretained{{.*}} : $@convention(c) () -> DefaultOwnershipConventionOnCXXForegnRefType.RefTypeDefaultUnretained
257+
258+
let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultUnRetained()
259+
// CHECK: function_ref {{.*}}returnRefTypeDefaultUnRetained{{.*}} : $@convention(c) () -> FunctionAnnotationHasPrecedence.RefTypeDefaultUnRetained
260+
261+
let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultUnRetainedAnnotatedRetained()
262+
// CHECK: function_ref {{.*}}returnRefTypeDefaultUnRetainedAnnotatedRetained{{.*}} : $@convention(c) () -> @owned FunctionAnnotationHasPrecedence.RefTypeDefaultUnRetained
263+
264+
let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultRetained()
265+
// CHECK: function_ref {{.*}}returnRefTypeDefaultRetained{{.*}} : $@convention(c) () -> @owned FunctionAnnotationHasPrecedence.RefTypeDefaultRetained
266+
267+
let _ = FunctionAnnotationHasPrecedence.returnRefTypeDefaultRetainedAnnotatedUnRetained()
268+
// CHECK: function_ref {{.*}}returnRefTypeDefaultRetainedAnnotatedUnRetained{{.*}} : $@convention(c) () -> FunctionAnnotationHasPrecedence.RefTypeDefaultRetained
269+
}

0 commit comments

Comments
 (0)