Skip to content

Commit ad4df73

Browse files
committed
[cxx-interop] Warning unannotated C++ APIs returning SWIFT_SHARED_REFERENCE types
1 parent e362264 commit ad4df73

File tree

7 files changed

+106
-34
lines changed

7 files changed

+106
-34
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,23 @@ ERROR(failed_base_method_call_synthesis,none,
258258
"failed to synthesize call to the base method %0 of type %0",
259259
(ValueDecl *, ValueDecl *))
260260

261-
ERROR(both_returns_retained_returns_unretained,none,
262-
"%0 cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes", (const clang::NamedDecl*))
261+
ERROR(both_returns_retained_returns_unretained, none,
262+
"%0 cannot be annotated with both swift_attr('returns_retained') and "
263+
"swift_attr('returns_unretained') attributes",
264+
(const clang::NamedDecl *))
265+
266+
ERROR(returns_retained_or_returns_unretained_for_non_cxx_frt_values, none,
267+
"%0 cannot be annotated with either swift_attr('returns_retained') or "
268+
"swift_attr('returns_unretained') attribute because it is not returning "
269+
"a 'SWIFT_SHARED_REFERENCE' type",
270+
(const clang::NamedDecl *))
271+
272+
WARNING(
273+
no_returns_retained_returns_unretained, none,
274+
"%0 is returning a 'SWIFT_SHARED_REFERENCE' type but is not annotated "
275+
"with either swift_attr('returns_retained') or "
276+
"swift_attr('returns_unretained') attributes",
277+
(const clang::NamedDecl *))
263278

264279
NOTE(unsupported_builtin_type, none, "built-in type '%0' not supported", (StringRef))
265280
NOTE(record_field_not_imported, none, "field %0 unavailable (cannot import)", (const clang::NamedDecl*))

include/swift/ClangImporter/ClangImporter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,9 @@ bool isCxxConstReferenceType(const clang::Type *type);
705705
/// Determine whether this typedef is a CF type.
706706
bool isCFTypeDecl(const clang::TypedefNameDecl *Decl);
707707

708+
// Determine whether type is a c++ foreign reference type.
709+
bool isForeignReferenceTypeWithoutImmortalAttrs(const clang::QualType type);
710+
708711
/// Determine the imported CF type for the given typedef-name, or the empty
709712
/// string if this is not an imported CF type name.
710713
llvm::StringRef getCFTypeName(const clang::TypedefNameDecl *decl);

lib/ClangImporter/ClangImporter.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7440,6 +7440,35 @@ static bool hasImportAsRefAttr(const clang::RecordDecl *decl) {
74407440
});
74417441
}
74427442

7443+
// TODO: Move all these utility functions in a new file ClangImporterUtils.h
7444+
// rdar://138803759
7445+
static bool hasImmortalAtts(const clang::RecordDecl *decl) {
7446+
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
7447+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
7448+
return swiftAttr->getAttribute() == "retain:immortal" ||
7449+
swiftAttr->getAttribute() == "release:immortal";
7450+
return false;
7451+
});
7452+
}
7453+
7454+
namespace swift {
7455+
namespace importer {
7456+
// Is this a pointer to a foreign reference type.
7457+
bool isForeignReferenceTypeWithoutImmortalAttrs(const clang::QualType type) {
7458+
if (!type->isPointerType())
7459+
return false;
7460+
7461+
auto pointeeType =
7462+
dyn_cast<clang::RecordType>(type->getPointeeType().getCanonicalType());
7463+
if (pointeeType == nullptr)
7464+
return false;
7465+
7466+
return hasImportAsRefAttr(pointeeType->getDecl()) &&
7467+
!hasImmortalAtts(pointeeType->getDecl());
7468+
}
7469+
} // namespace importer
7470+
} // namespace swift
7471+
74437472
// Is this a pointer to a foreign reference type.
74447473
static bool isForeignReferenceType(const clang::QualType type) {
74457474
if (!type->isPointerType())

lib/ClangImporter/ImportDecl.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/AST/ConformanceLookup.h"
2626
#include "swift/AST/Decl.h"
2727
#include "swift/AST/DiagnosticsClangImporter.h"
28+
#include "swift/ClangImporter/ClangImporter.h"
2829
#include "swift/AST/ExistentialLayout.h"
2930
#include "swift/AST/Expr.h"
3031
#include "swift/AST/GenericEnvironment.h"
@@ -96,6 +97,8 @@ struct AccessorInfo {
9697

9798
} // end anonymous namespace
9899

100+
// extern bool isForeignReferenceType(const clang::QualType type);
101+
99102
static bool isInSystemModule(const DeclContext *D) {
100103
return cast<ClangModuleUnit>(D->getModuleScopeContext())->isSystemModule();
101104
}
@@ -2685,7 +2688,7 @@ namespace {
26852688
Impl.diagnoseTopLevelValue(
26862689
DeclName(Impl.SwiftContext.getIdentifier(releaseOperation.name)));
26872690
}
2688-
}else if (releaseOperation.kind ==
2691+
} else if (releaseOperation.kind ==
26892692
CustomRefCountingOperationResult::tooManyFound) {
26902693
HeaderLoc loc(decl->getLocation());
26912694
Impl.diagnose(loc,
@@ -3353,13 +3356,11 @@ namespace {
33533356
return property->getParsedAccessor(AccessorKind::Set);
33543357
}
33553358

3356-
// If a C++ decl is annotated with both swift_attr("returns_retained") and
3357-
// swift_attr("returns_unretained") then emit an error in the swift
3358-
// compiler. Note: this error is not emitted in the clang compiler because
3359-
// these attributes are used only for swift interop.
3359+
// Emit diagnostics for incorrect usage of "returns_unretained" and
3360+
// "returns_unretained" attributes
3361+
bool returnsRetainedAttrIsPresent = false;
3362+
bool returnsUnretainedAttrIsPresent = false;
33603363
if (decl->hasAttrs()) {
3361-
bool returnsRetainedAttrIsPresent = false;
3362-
bool returnsUnretainedAttrIsPresent = false;
33633364
for (const auto *attr : decl->getAttrs()) {
33643365
if (const auto *swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
33653366
if (swiftAttr->getAttribute() == "returns_unretained") {
@@ -3369,11 +3370,27 @@ namespace {
33693370
}
33703371
}
33713372
}
3373+
}
33723374

3375+
HeaderLoc loc(decl->getLocation());
3376+
if (isForeignReferenceTypeWithoutImmortalAttrs(decl->getReturnType())) {
33733377
if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) {
3374-
HeaderLoc loc(decl->getLocation());
33753378
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
33763379
decl);
3380+
} else if (!returnsRetainedAttrIsPresent &&
3381+
!returnsUnretainedAttrIsPresent) {
3382+
Impl.diagnose(loc, diag::no_returns_retained_returns_unretained,
3383+
decl);
3384+
// TODO: make this case an error in next cxx-interop versions
3385+
// rdar://138806722
3386+
}
3387+
} else {
3388+
if (returnsRetainedAttrIsPresent || returnsUnretainedAttrIsPresent) {
3389+
Impl.diagnose(
3390+
loc,
3391+
diag::
3392+
returns_retained_or_returns_unretained_for_non_cxx_frt_values,
3393+
decl);
33773394
}
33783395
}
33793396

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

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,26 +160,31 @@ struct
160160

161161
struct NonFRTStruct {};
162162

163+
// C++ APIs returning FRTs but not having annotations
164+
struct
165+
StructWithoutReturnsAnnotations {
166+
static FRTStruct *_Nonnull StaticMethodReturningFRT();
167+
};
168+
169+
FRTStruct *_Nonnull global_function_returning_FRT_WithoutReturnsAnnotations();
170+
171+
// C++ APIs not returning FRTs but having annotations
172+
struct
173+
StructWithReturnsAnnotation {
174+
static NonFRTStruct *_Nonnull StaticMethodReturningNonFRT() __attribute__((swift_attr("returns_retained")));
175+
};
176+
177+
NonFRTStruct *_Nonnull global_function_returning_NON_FRT_WithReturnsAnnotations() __attribute__((swift_attr("returns_retained")));
178+
179+
163180
// Global/free C++ functions returning non-FRT
164181
NonFRTStruct *_Nonnull global_function_returning_non_FRT();
165-
NonFRTStruct
166-
*_Nonnull global_function_returning_non_FRT_with_attr_returns_retained()
167-
__attribute__((swift_attr("returns_retained")));
168-
NonFRTStruct
169-
*_Nonnull global_function_returning_non_FRT_with_attr_returns_unretained()
170-
__attribute__((swift_attr("returns_unretained")));
171182
NonFRTStruct *_Nonnull global_function_returning_non_FRT_create();
172183
NonFRTStruct *_Nonnull global_function_returning_non_FRT_copy();
173184

174185
// Struct having static method returning non-FRT
175186
struct StructWithStaticMethodsReturningNonFRT {
176187
static NonFRTStruct *_Nonnull StaticMethodReturningNonFRT();
177-
static NonFRTStruct
178-
*_Nonnull StaticMethodReturningNonFRTWithAttrReturnsRetained()
179-
__attribute__((swift_attr("returns_retained")));
180-
static NonFRTStruct
181-
*_Nonnull StaticMethodReturningNonFRTWithAttrReturnsUnretained()
182-
__attribute__((swift_attr("returns_unretained")));
183188
static NonFRTStruct *_Nonnull StaticMethodReturningNonFRT_create();
184189
static NonFRTStruct *_Nonnull StaticMethodReturningNonFRT_copy();
185190
};

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,18 @@ let frtLocalVar1 = global_function_returning_FRT_with_both_attrs_returns_retaine
88

99
let frtLocalVar2 = StructWithStaticMethodsReturningFRTWithBothAttributesReturnsRetainedAndReturnsUnretained.StaticMethodReturningFRT()
1010
// CHECK: error: 'StaticMethodReturningFRT' cannot be annotated with both swift_attr('returns_retained') and swift_attr('returns_unretained') attributes
11+
12+
let frtLocalVar3 = global_function_returning_FRT_WithoutReturnsAnnotations()
13+
// CHECK: warning: 'global_function_returning_FRT_WithoutReturnsAnnotations' is returning a 'SWIFT_SHARED_REFERENCE' type but is not annotated with either swift_attr('returns_retained') or swift_attr('returns_unretained') attributes
14+
15+
let frtLocalVar4 = StructWithoutReturnsAnnotations.StaticMethodReturningFRT()
16+
// CHECK: warning: 'StaticMethodReturningFRT' is returning a 'SWIFT_SHARED_REFERENCE' type but is not annotated with either swift_attr('returns_retained') or swift_attr('returns_unretained') attributes
17+
18+
let frtLocalVar5 = global_function_returning_NON_FRT_WithReturnsAnnotations()
19+
// CHECK: error: 'global_function_returning_NON_FRT_WithReturnsAnnotations' cannot be annotated with either swift_attr('returns_retained') or swift_attr('returns_unretained') attribute because it is not returning a 'SWIFT_SHARED_REFERENCE' type
20+
21+
let frtLocalVar6 = StructWithReturnsAnnotation.StaticMethodReturningNonFRT()
22+
// CHECK: error: 'StaticMethodReturningNonFRT' cannot be annotated with either swift_attr('returns_retained') or swift_attr('returns_unretained') attribute because it is not returning a 'SWIFT_SHARED_REFERENCE' type
23+
24+
// TODO: Add tests to check the absence of diagnostics when retrun type is SWIFT_IMMORTAL_REFERENCE or SWIFT_UNSAFE_REFERENCE
25+
// TODO: Add test to check the absence of diagnostics for certain cases when return type is not a C++ FRT

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,6 @@ func testFreeFunctionsReturningNonFRT() {
167167
let frtLocalVar1 = global_function_returning_non_FRT()
168168
// CHECK: function_ref @{{.*}}global_function_returning_non_FRT{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
169169

170-
let frtLocalVar2 = global_function_returning_non_FRT_with_attr_returns_retained()
171-
// CHECK: function_ref @{{.*}}global_function_returning_non_FRT_with_attr_returns_retained{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
172-
173-
let frtLocalVar3 = global_function_returning_non_FRT_with_attr_returns_unretained()
174-
// CHECK: function_ref @{{.*}}global_function_returning_non_FRT_with_attr_returns_unretained{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
175-
176170
let frtLocalVar4 = global_function_returning_non_FRT_create()
177171
// CHECK: function_ref @{{.*}}global_function_returning_non_FRT_create{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
178172

@@ -184,12 +178,6 @@ func testStaticMethodsReturningNonFRT() {
184178
let frtLocalVar1 = StructWithStaticMethodsReturningNonFRT.StaticMethodReturningNonFRT()
185179
// CHECK: function_ref @{{.*}}StaticMethodReturningNonFRT{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
186180

187-
let frtLocalVar2 = StructWithStaticMethodsReturningNonFRT.StaticMethodReturningNonFRTWithAttrReturnsRetained()
188-
// CHECK: function_ref @{{.*}}StaticMethodReturningNonFRTWithAttrReturnsRetained{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
189-
190-
let frtLocalVar3 = StructWithStaticMethodsReturningNonFRT.StaticMethodReturningNonFRTWithAttrReturnsUnretained()
191-
// CHECK: function_ref @{{.*}}StaticMethodReturningNonFRTWithAttrReturnsUnretained{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
192-
193181
let frtLocalVar4 = StructWithStaticMethodsReturningNonFRT.StaticMethodReturningNonFRT_create()
194182
// CHECK: function_ref @{{.*}}StaticMethodReturningNonFRT_create{{.*}} : $@convention(c) () -> UnsafeMutablePointer<NonFRTStruct>
195183

0 commit comments

Comments
 (0)