Skip to content

Commit c73f528

Browse files
committed
[cxx-interop] Validate C foreign reference types
Swift validates the retain/release operations for foreign reference types to check for obvious errors, e.g. a wrong parameter type or return type. That logic was only running for C++ foreign reference types. This patch enables it for C foreign reference types as well. rdar://158609723
1 parent d8edd86 commit c73f528

File tree

4 files changed

+66
-25
lines changed

4 files changed

+66
-25
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2692,6 +2692,25 @@ namespace {
26922692
}
26932693
}
26942694

2695+
if (auto classDecl = dyn_cast<ClassDecl>(result)) {
2696+
validateForeignReferenceType(decl, classDecl);
2697+
2698+
auto availability = Impl.SwiftContext.getSwift58Availability();
2699+
if (!availability.isAlwaysAvailable()) {
2700+
assert(availability.hasMinimumVersion());
2701+
auto AvAttr = AvailableAttr::createPlatformVersioned(
2702+
Impl.SwiftContext, targetPlatform(Impl.SwiftContext.LangOpts),
2703+
/*Message=*/"", /*Rename=*/"",
2704+
availability.getRawMinimumVersion(), /*Deprecated=*/{},
2705+
/*Obsoleted=*/{});
2706+
classDecl->getAttrs().add(AvAttr);
2707+
}
2708+
2709+
if (cxxRecordDecl && cxxRecordDecl->isEffectivelyFinal())
2710+
classDecl->getAttrs().add(new (Impl.SwiftContext)
2711+
FinalAttr(/*IsImplicit=*/true));
2712+
}
2713+
26952714
// If we need it, add an explicit "deinit" to this type.
26962715
synthesizer.addExplicitDeinitIfRequired(result, decl);
26972716

@@ -2737,7 +2756,7 @@ namespace {
27372756
}
27382757
}
27392758

2740-
void validateForeignReferenceType(const clang::CXXRecordDecl *decl,
2759+
void validateForeignReferenceType(const clang::RecordDecl *decl,
27412760
ClassDecl *classDecl) {
27422761

27432762
enum class RetainReleaseOperationKind {
@@ -2789,11 +2808,13 @@ namespace {
27892808
// The parameter of the retain/release function should be pointer to the
27902809
// same FRT or a base FRT.
27912810
if (paramDecl != classDecl) {
2792-
if (const clang::Decl *paramClangDecl = paramDecl->getClangDecl()) {
2793-
if (const auto *paramTypeDecl =
2794-
dyn_cast<clang::CXXRecordDecl>(paramClangDecl)) {
2795-
if (decl->isDerivedFrom(paramTypeDecl)) {
2796-
return RetainReleaseOperationKind::valid;
2811+
if (auto cxxDecl = dyn_cast<clang::CXXRecordDecl>(decl)) {
2812+
if (const clang::Decl *paramClangDecl = paramDecl->getClangDecl()) {
2813+
if (const auto *paramTypeDecl =
2814+
dyn_cast<clang::CXXRecordDecl>(paramClangDecl)) {
2815+
if (cxxDecl->isDerivedFrom(paramTypeDecl)) {
2816+
return RetainReleaseOperationKind::valid;
2817+
}
27972818
}
27982819
}
27992820
}
@@ -3140,25 +3161,6 @@ namespace {
31403161

31413162
validatePrivateFileIDAttributes(decl);
31423163

3143-
if (auto classDecl = dyn_cast<ClassDecl>(result)) {
3144-
validateForeignReferenceType(decl, classDecl);
3145-
3146-
auto availability = Impl.SwiftContext.getSwift58Availability();
3147-
if (!availability.isAlwaysAvailable()) {
3148-
assert(availability.hasMinimumVersion());
3149-
auto AvAttr = AvailableAttr::createPlatformVersioned(
3150-
Impl.SwiftContext, targetPlatform(Impl.SwiftContext.LangOpts),
3151-
/*Message=*/"", /*Rename=*/"",
3152-
availability.getRawMinimumVersion(), /*Deprecated=*/{},
3153-
/*Obsoleted=*/{});
3154-
classDecl->getAttrs().add(AvAttr);
3155-
}
3156-
3157-
if (decl->isEffectivelyFinal())
3158-
classDecl->getAttrs().add(new (Impl.SwiftContext)
3159-
FinalAttr(/*IsImplicit=*/true));
3160-
}
3161-
31623164
// If this module is declared as a C++ module, try to synthesize
31633165
// conformances to Swift protocols from the Cxx module.
31643166
auto clangModule = Impl.getClangOwningModule(result->getClangNode());
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <stdlib.h>
2+
3+
struct __attribute__((swift_attr("import_reference")))
4+
__attribute__((swift_attr("retain:nonexistent")))
5+
__attribute__((swift_attr("release:nonexistent"))) NonExistent {
6+
int value;
7+
};
8+
9+
struct __attribute__((swift_attr("import_reference"))) NoRetainRelease {
10+
int value;
11+
};
12+
13+
struct __attribute__((swift_attr("import_reference")))
14+
__attribute__((swift_attr("retain:badRetain")))
15+
__attribute__((swift_attr("release:badRelease"))) BadRetainRelease {
16+
int value;
17+
};
18+
19+
float badRetain(struct BadRetainRelease *v);
20+
void badRelease(struct BadRetainRelease *v, int i);

test/Interop/C/struct/Inputs/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ module ForeignReference {
77
header "foreign-reference.h"
88
}
99

10+
module ForeignReferenceInvalid {
11+
header "foreign-reference-invalid.h"
12+
}
13+
1014
module StructAsOptionSet {
1115
header "struct-as-option-set.h"
1216
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs -disable-availability-checking -diagnostic-style llvm 2>&1 | %FileCheck %s
2+
3+
import ForeignReferenceInvalid
4+
5+
// CHECK: error: cannot find retain function 'nonexistent' for reference type 'NonExistent'
6+
// CHECK: error: cannot find release function 'nonexistent' for reference type 'NonExistent'
7+
public func test(x: NonExistent) { }
8+
9+
// CHECK: error: reference type 'NoRetainRelease' must have 'retain:' Swift attribute
10+
// CHECK: error: reference type 'NoRetainRelease' must have 'release:' Swift attribute
11+
public func test(x: NoRetainRelease) { }
12+
13+
// 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
14+
// CHECK: error: specified release function 'badRelease' is invalid; release function must have exactly one argument of type 'BadRetainRelease'
15+
public func test(x: BadRetainRelease) { }

0 commit comments

Comments
 (0)