Skip to content

[cxx-interop] Diagnose ObjC APIs returning SWIFT_SHARED_REFERENCE types #78458

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
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
29 changes: 21 additions & 8 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3376,7 +3376,11 @@ namespace {

/// Emit diagnostics for incorrect usage of SWIFT_RETURNS_RETAINED and
/// SWIFT_RETURNS_UNRETAINED
void checkBridgingAttrs(const clang::FunctionDecl *decl) {
void checkBridgingAttrs(const clang::NamedDecl *decl) {
assert(isa<clang::FunctionDecl>(decl) ||
isa<clang::ObjCMethodDecl>(decl) &&
"checkBridgingAttrs called with a clang::NamedDecl which is "
"neither clang::FunctionDecl nor clang::ObjCMethodDecl");
bool returnsRetainedAttrIsPresent = false;
bool returnsUnretainedAttrIsPresent = false;
if (decl->hasAttrs()) {
Expand All @@ -3392,14 +3396,19 @@ namespace {
}

HeaderLoc loc(decl->getLocation());
if (isForeignReferenceTypeWithoutImmortalAttrs(decl->getReturnType())) {
const auto retType =
isa<clang::FunctionDecl>(decl)
? cast<clang::FunctionDecl>(decl)->getReturnType()
: cast<clang::ObjCMethodDecl>(decl)->getReturnType();
if (isForeignReferenceTypeWithoutImmortalAttrs(retType)) {
if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) {
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
decl);
} else if (!returnsRetainedAttrIsPresent &&
!returnsUnretainedAttrIsPresent) {
bool unannotatedCxxAPIWarningNeeded = false;
if (!isa<clang::CXXDeductionGuideDecl>(decl)) {
bool unannotatedAPIWarningNeeded = false;
if (isa<clang::FunctionDecl>(decl) &&
!isa<clang::CXXDeductionGuideDecl>(decl)) {
if (const auto *methodDecl = dyn_cast<clang::CXXMethodDecl>(decl)) {
// FIXME: In the future we should support SWIFT_RETURNS_RETAINED
// and SWIFT_RETURNS_UNRETAINED for overloaded C++ operators
Expand All @@ -3409,15 +3418,17 @@ namespace {
!methodDecl->isOverloadedOperator() &&
methodDecl->isUserProvided()) {
// normal c++ member method
unannotatedCxxAPIWarningNeeded = true;
unannotatedAPIWarningNeeded = true;
}
} else {
// global or top-level function
unannotatedCxxAPIWarningNeeded = true;
// global or top-level C/C++ function
unannotatedAPIWarningNeeded = true;
}
} else if (isa<clang::ObjCMethodDecl>(decl)) {
unannotatedAPIWarningNeeded = true;
}

if (unannotatedCxxAPIWarningNeeded) {
if (unannotatedAPIWarningNeeded) {
HeaderLoc loc(decl->getLocation());
Impl.diagnose(loc, diag::no_returns_retained_returns_unretained,
decl);
Expand Down Expand Up @@ -4560,6 +4571,8 @@ namespace {
if (!dc)
return nullptr;

checkBridgingAttrs(decl);

// While importing the DeclContext, we might have imported the decl
// itself.
auto Known = Impl.importDeclCached(decl, getVersion());
Expand Down
18 changes: 16 additions & 2 deletions test/Interop/Cxx/objc-correctness/Inputs/cxx-frt.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,42 @@ struct CxxRefType {
__attribute__((swift_attr("retain:retainCxxRefType")))
__attribute__((swift_attr("release:releaseCxxRefType")));

struct CxxValType {};

void retainCxxRefType(CxxRefType *_Nonnull b) {}
void releaseCxxRefType(CxxRefType *_Nonnull b) {}

@interface Bridge : NSObject

+ (struct CxxRefType *)objCMethodReturningFRTUnannotated;
+ (struct CxxRefType *)objCMethodReturningFRTUnannotated; // expected-warning {{'objCMethodReturningFRTUnannotated' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}}
+ (struct CxxRefType *)objCMethodReturningFRTUnowned
__attribute__((swift_attr("returns_unretained")));
+ (struct CxxRefType *)objCMethodReturningFRTOwned
__attribute__((swift_attr("returns_retained")));
+ (struct CxxRefType *)objCMethodReturningFRTBothAnnotations // expected-error {{'objCMethodReturningFRTBothAnnotations' cannot be annotated with both SWIFT_RETURNS_RETAINED and SWIFT_RETURNS_UNRETAINED}}
__attribute__((swift_attr("returns_unretained")))
__attribute__((swift_attr("returns_retained")));
+ (struct CxxValType *)objCMethodReturningNonCxxFrtAnannotated // expected-error {{'objCMethodReturningNonCxxFrtAnannotated' cannot be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED because it is not returning a SWIFT_SHARED_REFERENCE type}}
__attribute__((swift_attr("returns_retained")));

@end

@implementation Bridge
+ (struct CxxRefType *)objCMethodReturningFRTUnannotated {
};
}
+ (struct CxxRefType *)objCMethodReturningFRTUnowned
__attribute__((swift_attr("returns_unretained"))) {
}
+ (struct CxxRefType *)objCMethodReturningFRTOwned
__attribute__((swift_attr("returns_retained"))) {
}
+ (struct CxxRefType *)objCMethodReturningFRTBothAnnotations
__attribute__((swift_attr("returns_unretained")))
__attribute__((swift_attr("returns_retained"))) {
}
+ (struct CxxValType *)objCMethodReturningNonCxxFrtAnannotated
__attribute__((swift_attr("returns_retained"))) {
}

@end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: rm -rf %t
// RUN: %target-swift-frontend -typecheck -verify -I %S/Inputs %s -cxx-interoperability-mode=upcoming-swift -verify-additional-file %S/Inputs/cxx-frt.h -Xcc -Wno-return-type

import CxxForeignRef

// REQUIRES: objc_interop
func testObjCMethods() {
_ = Bridge.objCMethodReturningFRTBothAnnotations()
_ = Bridge.objCMethodReturningNonCxxFrtAnannotated()
_ = Bridge.objCMethodReturningFRTUnannotated()
}