Skip to content

[cxx-interop] Add SWIFT_RETURNS_RETAINED and SWIFT_RETURNS_UNRETAINED… #75897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsClangImporter.def
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ ERROR(failed_base_method_call_synthesis,none,
"failed to synthesize call to the base method %0 of type %0",
(ValueDecl *, ValueDecl *))

ERROR(both_returns_retained_returns_unretained,none,
"%0 cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes", (const clang::NamedDecl*))

NOTE(unsupported_builtin_type, none, "built-in type '%0' not supported", (StringRef))
NOTE(record_field_not_imported, none, "field %0 unavailable (cannot import)", (const clang::NamedDecl*))
NOTE(invoked_func_not_imported, none, "function %0 unavailable (cannot import)", (const clang::NamedDecl*))
Expand Down
24 changes: 24 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3262,6 +3262,30 @@ namespace {
return property->getParsedAccessor(AccessorKind::Set);
}

// If a C++ decl is annotated with both swift_attr("returns_retained") and
// swift_attr("returns_unretained") then emit an error in the swift
// compiler. Note: this error is not emitted in the clang compiler because
// these attributes are used only for swift interop.
if (decl->hasAttrs()) {
bool returnsRetainedAttrIsPresent = false;
bool returnsUnretainedAttrIsPresent = false;
for (const auto *attr : decl->getAttrs()) {
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
if (swiftAttr->getAttribute() == "returns_unretained") {
returnsUnretainedAttrIsPresent = true;
} else if (swiftAttr->getAttribute() == "returns_retained") {
returnsRetainedAttrIsPresent = true;
}
}
}

if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) {
HeaderLoc loc(decl->getLocation());
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
decl);
}
}

return importFunctionDecl(decl, importedName, correctSwiftName,
std::nullopt);
}
Expand Down
10 changes: 10 additions & 0 deletions lib/ClangImporter/SwiftBridging/swift/bridging
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@
#define SWIFT_NONESCAPABLE \
__attribute__((swift_attr("~Escapable")))

/// Specifies that the return value is passed as owned for C++ functions and
/// methods returning types annotated as `SWIFT_SHARED_REFERENCE`
#define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained")))
/// Specifies that the return value is passed as unowned for C++ functions and
/// methods returning types annotated as `SWIFT_SHARED_REFERENCE`
#define SWIFT_RETURNS_UNRETAINED \
__attribute__((swift_attr("returns_unretained")))

#else // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)

// Empty defines for compilers that don't support `attribute(swift_attr)`.
Expand All @@ -179,6 +187,8 @@
#define SWIFT_UNCHECKED_SENDABLE
#define SWIFT_NONCOPYABLE
#define SWIFT_NONESCAPABLE
#define SWIFT_RETURNS_RETAINED
#define SWIFT_RETURNS_UNRETAINED

#endif // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)

Expand Down
34 changes: 34 additions & 0 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3357,6 +3357,25 @@ class CFunctionTypeConventions : public Conventions {
const clang::FunctionType *type)
: Conventions(kind), FnType(type) {}

// Determines owned/unowned ResultConvention of the returned value based on
// returns_retained/returns_unretained attribute.
std::optional<ResultConvention>
getForeignReferenceTypeResultConventionWithAttributes(
const TypeLowering &tl, const clang::FunctionDecl *decl) const {
if (tl.getLoweredType().isForeignReferenceType() && decl->hasAttrs()) {
for (const auto *attr : decl->getAttrs()) {
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
if (swiftAttr->getAttribute() == "returns_unretained") {
return ResultConvention::Unowned;
} else if (swiftAttr->getAttribute() == "returns_retained") {
return ResultConvention::Owned;
}
}
}
}
return std::nullopt;
}

public:
CFunctionTypeConventions(const clang::FunctionType *type)
: Conventions(ConventionsKind::CFunctionType), FnType(type) {}
Expand Down Expand Up @@ -3474,6 +3493,13 @@ class CFunctionConventions : public CFunctionTypeConventions {
return ResultConvention::Indirect;
}

// Explicitly setting the ownership of the returned FRT if the C++
// global/free function has either swift_attr("returns_retained") or
// ("returns_unretained") attribute.
if (auto resultConventionOpt =
getForeignReferenceTypeResultConventionWithAttributes(tl, TheDecl))
return *resultConventionOpt;

if (isCFTypedef(tl, TheDecl->getReturnType())) {
// The CF attributes aren't represented in the type, so we need
// to check them here.
Expand Down Expand Up @@ -3552,6 +3578,14 @@ class CXXMethodConventions : public CFunctionTypeConventions {
// possible to make it easy for LLVM to optimize away the thunk.
return ResultConvention::Indirect;
}

// Explicitly setting the ownership of the returned FRT if the C++ member
// method has either swift_attr("returns_retained") or
// ("returns_unretained") attribute.
if (auto resultConventionOpt =
getForeignReferenceTypeResultConventionWithAttributes(resultTL, TheDecl))
return *resultConventionOpt;

if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&
resultTL.getLoweredType().isForeignReferenceType()) {
return ResultConvention::Owned;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
#pragma once

// FRT or SWIFT_SHARED_REFERENCE type
struct FRTStruct {
// Friend function declarations
friend FRTStruct *returnInstanceOfFRTStruct(int v);
friend FRTStruct *returnInstanceOfFRTStructWithAttrReturnsRetained(int v);
friend FRTStruct *returnInstanceOfFRTStructWithAttrReturnsUnretained(int v);
} __attribute__((swift_attr("import_reference")))
__attribute__((swift_attr("retain:retainFRTStruct")))
__attribute__((swift_attr("release:releaseFRTStruct")));

// Retain function for the FRT or SWIFT_SHARED_REFERENCE type FRTStruct
void retainFRTStruct(FRTStruct *_Nonnull b) {}

// Release function for the FRT or SWIFT_SHARED_REFERENCE type FRTStruct
void releaseFRTStruct(FRTStruct *_Nonnull b) {}

// Friend function definition
FRTStruct *returnInstanceOfFRTStruct() { return new FRTStruct; }

// Friend function definition
FRTStruct *returnInstanceOfFRTStructWithAttrReturnsRetained()
__attribute__((swift_attr("returns_retained"))) {
return new FRTStruct;
}

// Friend function definition
FRTStruct *returnInstanceOfFRTStructWithAttrReturnsUnretained()
__attribute__((swift_attr("returns_unretained"))) {
return new FRTStruct;
}

// Global/free C++ functions returning FRT without any attributes
FRTStruct *_Nonnull global_function_returning_FRT();
FRTStruct *_Nonnull global_function_returning_copy();
FRTStruct *_Nonnull global_function_returning_create();
FRTStruct *_Nonnull global_function_returning_init();
FRTStruct *_Nonnull global_function_returning_clone();

// Global/free C++ functions returning FRT with attribute
// swift_attr("returns_retained") or SWIFT_RETURNS_RETAINED
FRTStruct *_Nonnull global_function_returning_FRT_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));
FRTStruct *_Nonnull global_function_returning_copy_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));
FRTStruct
*_Nonnull global_function_returning_create_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));
FRTStruct *_Nonnull global_function_returning_init_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));
FRTStruct *_Nonnull global_function_returning_clone_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));

// Global/free C++ functions returning FRT with attribute
// swift_attr("returns_unretained") or SWIFT_RETURNS_UNRETAINED
FRTStruct *_Nonnull global_function_returning_FRT_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));
FRTStruct
*_Nonnull global_function_returning_copy_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));
FRTStruct
*_Nonnull global_function_returning_create_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));
FRTStruct
*_Nonnull global_function_returning_init_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));
FRTStruct
*_Nonnull global_function_returning_clone_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));

// Static Global/free functions returning FRT
static FRTStruct *_Nonnull global_static_function_returning_FRT();
static FRTStruct
*_Nonnull global_static_function_returning_FRT_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));
static FRTStruct
*_Nonnull global_static_function_returning_FRT_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));
static FRTStruct *_Nonnull global_static_function_returning_copy();
static FRTStruct *_Nonnull global_static_function_returning_create();
static FRTStruct
*_Nonnull global_static_function_returning_copy_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));
static FRTStruct
*_Nonnull global_static_function_returning_copy_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));

// Global/free functions returning FRT without _Nonnull
FRTStruct *global_function_returning_FRT_wihout_Nonnull();
FRTStruct *
global_function_returning_FRT_with_attr_returns_retained_wihout_Nonnull()
__attribute__((swift_attr("returns_retained")));
FRTStruct *
global_function_returning_FRT_with_attr_returns_unretained_wihout_Nonnull()
__attribute__((swift_attr("returns_unretained")));
FRTStruct *global_function_returning_copy_wihout_Nonnull();
FRTStruct *global_function_returning_create_wihout_Nonnull();
FRTStruct *
global_function_returning_copy_with_attr_returns_retained_wihout_Nonnull()
__attribute__((swift_attr("returns_retained")));
FRTStruct *
global_function_returning_copy_with_attr_returns_unretained_wihout_Nonnull()
__attribute__((swift_attr("returns_unretained")));

// Struct having static methods returning FRT without any attributes
struct StructWithStaticMethodsReturningFRTWithoutAttributes {
static FRTStruct *_Nonnull StaticMethodReturningFRT();
static FRTStruct *_Nonnull StaticMethodReturningFRT_copy();
static FRTStruct *_Nonnull StaticMethodReturningFRT_create();
static FRTStruct *_Nonnull StaticMethodReturningFRT_init();
static FRTStruct *_Nonnull StaticMethodReturningFRT_clone();
};

// Struct having static methods returning FRT with attribute
// swift_attr("returns_retained") or SWIFT_RETURNS_RETAINED
struct StructWithStaticMethodsReturningFRTWithAttributeReturnsRetained {
static FRTStruct *_Nonnull StaticMethodReturningFRT()
__attribute__((swift_attr("returns_retained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_copy()
__attribute__((swift_attr("returns_retained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_create()
__attribute__((swift_attr("returns_retained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_init()
__attribute__((swift_attr("returns_retained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_clone()
__attribute__((swift_attr("returns_retained")));
};

// Struct having static methods returning FRT with attribute
// swift_attr("returns_unretained") or SWIFT_RETURNS_UNRETAINED
struct StructWithStaticMethodsReturningFRTWithAttributeReturnsUnretained {
static FRTStruct *_Nonnull StaticMethodReturningFRT()
__attribute__((swift_attr("returns_unretained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_copy()
__attribute__((swift_attr("returns_unretained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_create()
__attribute__((swift_attr("returns_unretained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_init()
__attribute__((swift_attr("returns_unretained")));
static FRTStruct *_Nonnull StaticMethodReturningFRT_clone()
__attribute__((swift_attr("returns_unretained")));
};

// Global/free C++ functions returning FRT with both attributes
// swift_attr("returns_unretained") and swift_attr("returns_retained")
FRTStruct
*_Nonnull global_function_returning_FRT_with_both_attrs_returns_retained_returns_unretained()
__attribute__((swift_attr("returns_retained")))
__attribute__((swift_attr("returns_unretained")));

// Struct having static method returning FRT with both attributes
// swift_attr("returns_unretained") and swift_attr("returns_retained")
struct
StructWithStaticMethodsReturningFRTWithBothAttributesReturnsRetainedAndReturnsUnretained {
static FRTStruct *_Nonnull StaticMethodReturningFRT()
__attribute__((swift_attr("returns_retained")))
__attribute__((swift_attr("returns_unretained")));
};

struct NonFRTStruct {};

// Global/free C++ functions returning non-FRT
NonFRTStruct *_Nonnull global_function_returning_non_FRT();
NonFRTStruct
*_Nonnull global_function_returning_non_FRT_with_attr_returns_retained()
__attribute__((swift_attr("returns_retained")));
NonFRTStruct
*_Nonnull global_function_returning_non_FRT_with_attr_returns_unretained()
__attribute__((swift_attr("returns_unretained")));
NonFRTStruct *_Nonnull global_function_returning_non_FRT_create();
NonFRTStruct *_Nonnull global_function_returning_non_FRT_copy();

// Struct having static method returning non-FRT
struct StructWithStaticMethodsReturningNonFRT {
static NonFRTStruct *_Nonnull StaticMethodReturningNonFRT();
static NonFRTStruct
*_Nonnull StaticMethodReturningNonFRTWithAttrReturnsRetained()
__attribute__((swift_attr("returns_retained")));
static NonFRTStruct
*_Nonnull StaticMethodReturningNonFRTWithAttrReturnsUnretained()
__attribute__((swift_attr("returns_unretained")));
static NonFRTStruct *_Nonnull StaticMethodReturningNonFRT_create();
static NonFRTStruct *_Nonnull StaticMethodReturningNonFRT_copy();
};

// Tests for templated functions
template <typename T>
FRTStruct *_Nonnull global_templated_function_returning_FRT(T a);

template <typename T>
FRTStruct *_Nonnull global_templated_function_returning_FRT_copy(T a);

template <typename T>
FRTStruct *_Nonnull global_templated_function_returning_FRT_create(T a);

template <typename T>
FRTStruct *_Nonnull global_templated_function_returning_FRT_init(T a);

template <typename T>
FRTStruct *_Nonnull global_templated_function_returning_FRT_clone(T a);

template <typename T>
FRTStruct
*_Nonnull global_templated_function_returning_FRT_with_attr_returns_retained(
T a) __attribute__((swift_attr("returns_retained")));

template <typename T>
FRTStruct
*_Nonnull global_templated_function_returning_FRT_copy_with_attr_returns_retained(
T a) __attribute__((swift_attr("returns_retained")));

template <typename T>
FRTStruct
*_Nonnull global_templated_function_returning_FRT_create_with_attr_returns_retained(
T a) __attribute__((swift_attr("returns_retained")));

template <typename T>
FRTStruct
*_Nonnull global_templated_function_returning_FRT_with_attr_returns_unretained(
T a) __attribute__((swift_attr("returns_unretained")));

template <typename T>
FRTStruct
*_Nonnull global_templated_function_returning_FRT_copy_with_attr_returns_unretained(
T a) __attribute__((swift_attr("returns_unretained")));

template <typename T>
FRTStruct
*_Nonnull global_templated_function_returning_FRT_create_with_attr_returns_unretained(
T a) __attribute__((swift_attr("returns_unretained")));
4 changes: 4 additions & 0 deletions test/Interop/Cxx/foreign-reference/Inputs/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ module DerivedFieldGetterReturnsOwnedFRT {
requires cplusplus
}

module FunctionsAndMethodsReturningFRT {
header "cxx-functions-and-methods-returning-frt.h"
requires cplusplus
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: rm -rf %t
// RUN: not %target-swift-frontend -typecheck -I %S/Inputs %s -cxx-interoperability-mode=upcoming-swift 2>&1 | %FileCheck %s

import FunctionsAndMethodsReturningFRT

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

let frtLocalVar2 = StructWithStaticMethodsReturningFRTWithBothAttributesReturnsRetainedAndReturnsUnretained.StaticMethodReturningFRT()
// CHECK: error: 'StaticMethodReturningFRT' cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes
Loading