Skip to content

Commit 9c5de6f

Browse files
committed
[cxx-interop] Allow retain/release operations to be methods
Some foreign reference types such as IUnknown define retain/release operations as methods of the type. Previously Swift only supported retain/release operations as standalone functions. The syntax for member functions would be `SWIFT_SHARED_REFERENCE(.doRetain, .doRelease)`. rdar://160696723
1 parent 2bdc1f4 commit 9c5de6f

11 files changed

+325
-16
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ ERROR(foreign_reference_types_release_non_void_return_type, none,
270270
ERROR(foreign_reference_types_retain_release_not_a_function_decl, none,
271271
"specified %select{retain|release}0 function '%1' is not a function",
272272
(bool, StringRef))
273+
ERROR(foreign_reference_types_retain_release_not_an_instance_function, none,
274+
"specified %select{retain|release}0 function '%1' is a static function; expected an instance function",
275+
(bool, StringRef))
273276
ERROR(conforms_to_missing_dot, none,
274277
"expected module name and protocol name separated by '.' in protocol "
275278
"conformance; '%0' is invalid",

lib/ClangImporter/ClangImporter.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7915,10 +7915,27 @@ getRefParentDecls(const clang::RecordDecl *decl, ASTContext &ctx,
79157915
}
79167916

79177917
llvm::SmallVector<ValueDecl *, 1>
7918-
importer::getValueDeclsForName(
7919-
const clang::Decl *decl, ASTContext &ctx, StringRef name) {
7918+
importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
7919+
// If the name is empty, don't try to find any decls.
7920+
if (name.empty())
7921+
return {};
7922+
7923+
auto &ctx = decl->getASTContext();
7924+
auto clangDecl = decl->getClangDecl();
79207925
llvm::SmallVector<ValueDecl *, 1> results;
7921-
auto *clangMod = decl->getOwningModule();
7926+
7927+
if (name.starts_with(".")) {
7928+
// Look for a member of decl instead of a global.
7929+
StringRef memberName = name.drop_front(1);
7930+
if (memberName.empty())
7931+
return {};
7932+
auto declName = DeclName(ctx.getIdentifier(memberName));
7933+
auto allResults = evaluateOrDefault(
7934+
ctx.evaluator, ClangRecordMemberLookup({decl, declName}), {});
7935+
return SmallVector<ValueDecl *, 1>(allResults.begin(), allResults.end());
7936+
}
7937+
7938+
auto *clangMod = clangDecl->getOwningModule();
79227939
if (clangMod && clangMod->isSubModule())
79237940
clangMod = clangMod->getTopLevelModule();
79247941
if (clangMod) {
@@ -8523,7 +8540,7 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
85238540
return {CustomRefCountingOperationResult::immortal, nullptr, name};
85248541

85258542
llvm::SmallVector<ValueDecl *, 1> results =
8526-
getValueDeclsForName(swiftDecl->getClangDecl(), ctx, name);
8543+
getValueDeclsForName(const_cast<ClassDecl*>(swiftDecl), name);
85278544
if (results.size() == 1)
85288545
return {CustomRefCountingOperationResult::foundOperation, results.front(),
85298546
name};

lib/ClangImporter/ImportDecl.cpp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,7 @@ namespace {
27882788

27892789
enum class RetainReleaseOperationKind {
27902790
notAfunction,
2791+
notAnInstanceFunction,
27912792
invalidReturnType,
27922793
invalidParameters,
27932794
valid
@@ -2801,17 +2802,32 @@ namespace {
28012802
if (!operationFn)
28022803
return RetainReleaseOperationKind::notAfunction;
28032804

2804-
if (operationFn->getParameters()->size() != 1)
2805-
return RetainReleaseOperationKind::invalidParameters;
2805+
if (operationFn->isStatic())
2806+
return RetainReleaseOperationKind::notAnInstanceFunction;
28062807

2807-
Type paramType =
2808-
operationFn->getParameters()->get(0)->getInterfaceType();
2809-
// Unwrap if paramType is an OptionalType
2810-
if (Type optionalType = paramType->getOptionalObjectType()) {
2811-
paramType = optionalType;
2812-
}
2808+
if (operationFn->isInstanceMember()) {
2809+
if (operationFn->getParameters()->size() != 0)
2810+
return RetainReleaseOperationKind::invalidParameters;
2811+
} else {
2812+
if (operationFn->getParameters()->size() != 1)
2813+
return RetainReleaseOperationKind::invalidParameters;
2814+
}
2815+
2816+
Type paramType;
2817+
NominalTypeDecl *paramDecl = nullptr;
2818+
if (!operationFn->isInstanceMember()) {
2819+
paramType =
2820+
operationFn->getParameters()->get(0)->getInterfaceType();
2821+
// Unwrap if paramType is an OptionalType
2822+
if (Type optionalType = paramType->getOptionalObjectType()) {
2823+
paramType = optionalType;
2824+
}
28132825

2814-
swift::NominalTypeDecl *paramDecl = paramType->getAnyNominal();
2826+
paramDecl = paramType->getAnyNominal();
2827+
} else {
2828+
paramDecl = cast<NominalTypeDecl>(operationFn->getParent());
2829+
paramType = paramDecl->getDeclaredInterfaceType();
2830+
}
28152831

28162832
// The return type should be void (for release functions), or void
28172833
// or the parameter type (for retain functions).
@@ -2896,6 +2912,12 @@ namespace {
28962912
diag::foreign_reference_types_retain_release_not_a_function_decl,
28972913
false, retainOperation.name);
28982914
break;
2915+
case RetainReleaseOperationKind::notAnInstanceFunction:
2916+
Impl.diagnose(
2917+
loc,
2918+
diag::foreign_reference_types_retain_release_not_an_instance_function,
2919+
false, retainOperation.name);
2920+
break;
28992921
case RetainReleaseOperationKind::invalidReturnType:
29002922
Impl.diagnose(
29012923
loc,
@@ -2961,6 +2983,12 @@ namespace {
29612983
diag::foreign_reference_types_retain_release_not_a_function_decl,
29622984
true, releaseOperation.name);
29632985
break;
2986+
case RetainReleaseOperationKind::notAnInstanceFunction:
2987+
Impl.diagnose(
2988+
loc,
2989+
diag::foreign_reference_types_retain_release_not_an_instance_function,
2990+
true, releaseOperation.name);
2991+
break;
29642992
case RetainReleaseOperationKind::invalidReturnType:
29652993
Impl.diagnose(
29662994
loc,

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2200,7 +2200,7 @@ ImportedType findOptionSetEnum(clang::QualType type,
22002200
///
22012201
/// The name we're looking for is the Swift name.
22022202
llvm::SmallVector<ValueDecl *, 1>
2203-
getValueDeclsForName(const clang::Decl *decl, ASTContext &ctx, StringRef name);
2203+
getValueDeclsForName(NominalTypeDecl* decl, StringRef name);
22042204

22052205
} // end namespace importer
22062206
} // end namespace swift

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2882,8 +2882,7 @@ FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy(
28822882
if (!destroyFuncName.consume_front("destroy:"))
28832883
continue;
28842884

2885-
auto decls = getValueDeclsForName(
2886-
clangType, nominal->getASTContext(), destroyFuncName);
2885+
auto decls = getValueDeclsForName(nominal, destroyFuncName);
28872886
for (auto decl : decls) {
28882887
auto func = dyn_cast<FuncDecl>(decl);
28892888
if (!func)

lib/IRGen/GenObjC.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,11 @@ void IRGenFunction::emitBlockRelease(llvm::Value *value) {
17241724

17251725
void IRGenFunction::emitForeignReferenceTypeLifetimeOperation(
17261726
ValueDecl *fn, llvm::Value *value, bool needsNullCheck) {
1727+
if (auto originalDecl = fn->getASTContext()
1728+
.getClangModuleLoader()
1729+
->getOriginalForClonedMember(fn))
1730+
fn = originalDecl;
1731+
17271732
assert(fn->getClangDecl() && isa<clang::FunctionDecl>(fn->getClangDecl()));
17281733

17291734
auto clangFn = cast<clang::FunctionDecl>(fn->getClangDecl());
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include <swift/bridging>
2+
3+
struct RefCountedBox {
4+
int value;
5+
int refCount = 1;
6+
7+
RefCountedBox(int value) : value(value) {}
8+
9+
void doRetain() { refCount++; }
10+
void doRelease() { refCount--; }
11+
} SWIFT_SHARED_REFERENCE(.doRetain, .doRelease);
12+
13+
struct DerivedRefCountedBox : RefCountedBox {
14+
int secondValue = 1;
15+
DerivedRefCountedBox(int value, int secondValue)
16+
: RefCountedBox(value), secondValue(secondValue) {}
17+
};
18+
19+
// MARK: Retain in a base type, release in derived
20+
21+
struct BaseHasRetain {
22+
mutable int refCount = 1;
23+
void doRetainInBase() const { refCount++; }
24+
};
25+
26+
struct DerivedHasRelease : BaseHasRetain {
27+
int value;
28+
DerivedHasRelease(int value) : value(value) {}
29+
30+
void doRelease() const { refCount--; }
31+
} SWIFT_SHARED_REFERENCE(.doRetainInBase, .doRelease);
32+
33+
// MARK: Retain in a base type, release in templated derived
34+
35+
template <typename T>
36+
struct TemplatedDerivedHasRelease : BaseHasRetain {
37+
T value;
38+
TemplatedDerivedHasRelease(T value) : value(value) {}
39+
40+
void doReleaseTemplated() const { refCount--; }
41+
} SWIFT_SHARED_REFERENCE(.doRetainInBase, .doReleaseTemplated);
42+
43+
using TemplatedDerivedHasReleaseFloat = TemplatedDerivedHasRelease<float>;
44+
using TemplatedDerivedHasReleaseInt = TemplatedDerivedHasRelease<int>;
45+
46+
// MARK: Retain/release in CRTP base type
47+
48+
template <typename Derived>
49+
struct CRTPBase {
50+
mutable int refCount = 1;
51+
void crtpRetain() const { refCount++; }
52+
void crtpRelease() const { refCount--; }
53+
} SWIFT_SHARED_REFERENCE(.crtpRetain, .crtpRelease);
54+
55+
struct CRTPDerived : CRTPBase<CRTPDerived> {
56+
int value;
57+
CRTPDerived(int value) : value(value) {}
58+
};
59+
60+
// MARK: Virtual retain and release
61+
62+
struct VirtualRetainRelease {
63+
int value;
64+
mutable int refCount = 1;
65+
VirtualRetainRelease(int value) : value(value) {}
66+
67+
virtual void doRetainVirtual() const { refCount++; }
68+
virtual void doReleaseVirtual() const { refCount--; }
69+
virtual ~VirtualRetainRelease() = default;
70+
} SWIFT_SHARED_REFERENCE(.doRetainVirtual, .doReleaseVirtual);
71+
72+
struct DerivedVirtualRetainRelease : VirtualRetainRelease {
73+
DerivedVirtualRetainRelease(int value) : VirtualRetainRelease(value) {}
74+
75+
mutable bool calledDerived = false;
76+
void doRetainVirtual() const override { refCount++; calledDerived = true; }
77+
void doReleaseVirtual() const override { refCount--; }
78+
};
79+
80+
// MARK: Pure virtual retain and release
81+
82+
struct PureVirtualRetainRelease {
83+
int value;
84+
mutable int refCount = 1;
85+
PureVirtualRetainRelease(int value) : value(value) {}
86+
87+
virtual void doRetainPure() const = 0;
88+
virtual void doReleasePure() const = 0;
89+
virtual ~PureVirtualRetainRelease() = default;
90+
} SWIFT_SHARED_REFERENCE(.doRetainPure, .doReleasePure);
91+
92+
struct DerivedPureVirtualRetainRelease : PureVirtualRetainRelease {
93+
mutable int refCount = 1;
94+
95+
DerivedPureVirtualRetainRelease(int value) : PureVirtualRetainRelease(value) {}
96+
void doRetainPure() const override { refCount++; }
97+
void doReleasePure() const override { refCount--; }
98+
};
99+
100+
// MARK: Static retain/release
101+
#ifdef INCORRECT
102+
struct StaticRetainRelease {
103+
// expected-error@-1 {{specified retain function '.staticRetain' is a static function; expected an instance function}}
104+
// expected-error@-2 {{specified release function '.staticRelease' is a static function; expected an instance function}}
105+
int value;
106+
int refCount = 1;
107+
108+
StaticRetainRelease(int value) : value(value) {}
109+
110+
static void staticRetain(StaticRetainRelease* o) { o->refCount++; }
111+
static void staticRelease(StaticRetainRelease* o) { o->refCount--; }
112+
} SWIFT_SHARED_REFERENCE(.staticRetain, .staticRelease);
113+
114+
struct DerivedStaticRetainRelease : StaticRetainRelease {
115+
// expected-error@-1 {{cannot find retain function '.staticRetain' for reference type 'DerivedStaticRetainRelease'}}
116+
// expected-error@-2 {{cannot find release function '.staticRelease' for reference type 'DerivedStaticRetainRelease'}}
117+
int secondValue = 1;
118+
DerivedStaticRetainRelease(int value, int secondValue)
119+
: StaticRetainRelease(value), secondValue(secondValue) {}
120+
};
121+
#endif

test/Interop/Cxx/foreign-reference/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ module ReferenceCountedObjCProperty {
4343
export *
4444
}
4545

46+
module LifetimeOperationMethods {
47+
header "lifetime-operation-methods.h"
48+
requires cplusplus
49+
}
50+
4651
module MemberLayout {
4752
header "member-layout.h"
4853
requires cplusplus
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %target-swift-ide-test -print-module -cxx-interoperability-mode=upcoming-swift -I %swift_src_root/lib/ClangImporter/SwiftBridging -module-to-print=LifetimeOperationMethods -I %S/Inputs -source-filename=x | %FileCheck %s
2+
3+
// CHECK: class RefCountedBox {
4+
// CHECK: func doRetain()
5+
// CHECK: func doRelease()
6+
// CHECK: }
7+
// CHECK: class DerivedRefCountedBox {
8+
// CHECK: func doRetain()
9+
// CHECK: func doRelease()
10+
// CHECK: }
11+
12+
// CHECK: class DerivedHasRelease {
13+
// CHECK: func doRelease()
14+
// CHECK: func doRetainInBase()
15+
// CHECK: }
16+
17+
// CHECK: class TemplatedDerivedHasRelease<CFloat> {
18+
// CHECK: var value: Float
19+
// CHECK: func doReleaseTemplated()
20+
// CHECK: func doRetainInBase()
21+
// CHECK: }
22+
// CHECK: class TemplatedDerivedHasRelease<CInt> {
23+
// CHECK: var value: Int32
24+
// CHECK: func doReleaseTemplated()
25+
// CHECK: func doRetainInBase()
26+
// CHECK: }
27+
28+
// CHECK: class CRTPDerived {
29+
// CHECK: var value: Int32
30+
// CHECK: }
31+
32+
// CHECK: class VirtualRetainRelease {
33+
// CHECK: func doRetainVirtual()
34+
// CHECK: func doReleaseVirtual()
35+
// CHECK: }
36+
// CHECK: class DerivedVirtualRetainRelease {
37+
// CHECK: func doRetainVirtual()
38+
// CHECK: func doReleaseVirtual()
39+
// CHECK: }
40+
41+
// CHECK: class PureVirtualRetainRelease {
42+
// CHECK: func doRetainPure()
43+
// CHECK: func doReleasePure()
44+
// CHECK: }
45+
// CHECK: class DerivedPureVirtualRetainRelease {
46+
// CHECK: func doRetainPure()
47+
// CHECK: func doReleasePure()
48+
// CHECK: var refCount: Int32
49+
// CHECK: }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %target-typecheck-verify-swift -Xcc -DINCORRECT -I %S/Inputs -I %swift_src_root/lib/ClangImporter/SwiftBridging -verify-additional-file %S%{fs-sep}Inputs%{fs-sep}lifetime-operation-methods.h -cxx-interoperability-mode=upcoming-swift -disable-availability-checking
2+
3+
import LifetimeOperationMethods
4+
5+
let _ = StaticRetainRelease(123)
6+
let _ = DerivedStaticRetainRelease(123, 456)

0 commit comments

Comments
 (0)