Skip to content

Commit c5c33d6

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 (cherry picked from commit e78ce61)
1 parent 9016636 commit c5c33d6

File tree

11 files changed

+325
-16
lines changed

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
@@ -265,6 +265,9 @@ ERROR(foreign_reference_types_release_non_void_return_type, none,
265265
ERROR(foreign_reference_types_retain_release_not_a_function_decl, none,
266266
"specified %select{retain|release}0 function '%1' is not a function",
267267
(bool, StringRef))
268+
ERROR(foreign_reference_types_retain_release_not_an_instance_function, none,
269+
"specified %select{retain|release}0 function '%1' is a static function; expected an instance function",
270+
(bool, StringRef))
268271
ERROR(conforms_to_missing_dot, none,
269272
"expected module name and protocol name separated by '.' in protocol "
270273
"conformance; '%0' is invalid",

lib/ClangImporter/ClangImporter.cpp

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

78787878
llvm::SmallVector<ValueDecl *, 1>
7879-
importer::getValueDeclsForName(
7880-
const clang::Decl *decl, ASTContext &ctx, StringRef name) {
7879+
importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
7880+
// If the name is empty, don't try to find any decls.
7881+
if (name.empty())
7882+
return {};
7883+
7884+
auto &ctx = decl->getASTContext();
7885+
auto clangDecl = decl->getClangDecl();
78817886
llvm::SmallVector<ValueDecl *, 1> results;
7882-
auto *clangMod = decl->getOwningModule();
7887+
7888+
if (name.starts_with(".")) {
7889+
// Look for a member of decl instead of a global.
7890+
StringRef memberName = name.drop_front(1);
7891+
if (memberName.empty())
7892+
return {};
7893+
auto declName = DeclName(ctx.getIdentifier(memberName));
7894+
auto allResults = evaluateOrDefault(
7895+
ctx.evaluator, ClangRecordMemberLookup({decl, declName}), {});
7896+
return SmallVector<ValueDecl *, 1>(allResults.begin(), allResults.end());
7897+
}
7898+
7899+
auto *clangMod = clangDecl->getOwningModule();
78837900
if (clangMod && clangMod->isSubModule())
78847901
clangMod = clangMod->getTopLevelModule();
78857902
if (clangMod) {
@@ -8487,7 +8504,7 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
84878504
return {CustomRefCountingOperationResult::immortal, nullptr, name};
84888505

84898506
llvm::SmallVector<ValueDecl *, 1> results =
8490-
getValueDeclsForName(swiftDecl->getClangDecl(), ctx, name);
8507+
getValueDeclsForName(const_cast<ClassDecl*>(swiftDecl), name);
84918508
if (results.size() == 1)
84928509
return {CustomRefCountingOperationResult::foundOperation, results.front(),
84938510
name};

lib/ClangImporter/ImportDecl.cpp

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

27482748
enum class RetainReleaseOperationKind {
27492749
notAfunction,
2750+
notAnInstanceFunction,
27502751
invalidReturnType,
27512752
invalidParameters,
27522753
valid
@@ -2760,17 +2761,32 @@ namespace {
27602761
if (!operationFn)
27612762
return RetainReleaseOperationKind::notAfunction;
27622763

2763-
if (operationFn->getParameters()->size() != 1)
2764-
return RetainReleaseOperationKind::invalidParameters;
2764+
if (operationFn->isStatic())
2765+
return RetainReleaseOperationKind::notAnInstanceFunction;
27652766

2766-
Type paramType =
2767-
operationFn->getParameters()->get(0)->getInterfaceType();
2768-
// Unwrap if paramType is an OptionalType
2769-
if (Type optionalType = paramType->getOptionalObjectType()) {
2770-
paramType = optionalType;
2771-
}
2767+
if (operationFn->isInstanceMember()) {
2768+
if (operationFn->getParameters()->size() != 0)
2769+
return RetainReleaseOperationKind::invalidParameters;
2770+
} else {
2771+
if (operationFn->getParameters()->size() != 1)
2772+
return RetainReleaseOperationKind::invalidParameters;
2773+
}
2774+
2775+
Type paramType;
2776+
NominalTypeDecl *paramDecl = nullptr;
2777+
if (!operationFn->isInstanceMember()) {
2778+
paramType =
2779+
operationFn->getParameters()->get(0)->getInterfaceType();
2780+
// Unwrap if paramType is an OptionalType
2781+
if (Type optionalType = paramType->getOptionalObjectType()) {
2782+
paramType = optionalType;
2783+
}
27722784

2773-
swift::NominalTypeDecl *paramDecl = paramType->getAnyNominal();
2785+
paramDecl = paramType->getAnyNominal();
2786+
} else {
2787+
paramDecl = cast<NominalTypeDecl>(operationFn->getParent());
2788+
paramType = paramDecl->getDeclaredInterfaceType();
2789+
}
27742790

27752791
// The return type should be void (for release functions), or void
27762792
// or the parameter type (for retain functions).
@@ -2855,6 +2871,12 @@ namespace {
28552871
diag::foreign_reference_types_retain_release_not_a_function_decl,
28562872
false, retainOperation.name);
28572873
break;
2874+
case RetainReleaseOperationKind::notAnInstanceFunction:
2875+
Impl.diagnose(
2876+
loc,
2877+
diag::foreign_reference_types_retain_release_not_an_instance_function,
2878+
false, retainOperation.name);
2879+
break;
28582880
case RetainReleaseOperationKind::invalidReturnType:
28592881
Impl.diagnose(
28602882
loc,
@@ -2920,6 +2942,12 @@ namespace {
29202942
diag::foreign_reference_types_retain_release_not_a_function_decl,
29212943
true, releaseOperation.name);
29222944
break;
2945+
case RetainReleaseOperationKind::notAnInstanceFunction:
2946+
Impl.diagnose(
2947+
loc,
2948+
diag::foreign_reference_types_retain_release_not_an_instance_function,
2949+
true, releaseOperation.name);
2950+
break;
29232951
case RetainReleaseOperationKind::invalidReturnType:
29242952
Impl.diagnose(
29252953
loc,

lib/ClangImporter/ImporterImpl.h

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

21562156
} // end namespace importer
21572157
} // end namespace swift

lib/ClangImporter/SwiftDeclSynthesizer.cpp

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

2770-
auto decls = getValueDeclsForName(
2771-
clangType, nominal->getASTContext(), destroyFuncName);
2770+
auto decls = getValueDeclsForName(nominal, destroyFuncName);
27722771
for (auto decl : decls) {
27732772
auto func = dyn_cast<FuncDecl>(decl);
27742773
if (!func)

lib/IRGen/GenObjC.cpp

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

17231723
void IRGenFunction::emitForeignReferenceTypeLifetimeOperation(
17241724
ValueDecl *fn, llvm::Value *value, bool needsNullCheck) {
1725+
if (auto originalDecl = fn->getASTContext()
1726+
.getClangModuleLoader()
1727+
->getOriginalForClonedMember(fn))
1728+
fn = originalDecl;
1729+
17251730
assert(fn->getClangDecl() && isa<clang::FunctionDecl>(fn->getClangDecl()));
17261731

17271732
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
@@ -44,6 +44,11 @@ module ReferenceCountedObjCProperty {
4444
export *
4545
}
4646

47+
module LifetimeOperationMethods {
48+
header "lifetime-operation-methods.h"
49+
requires cplusplus
50+
}
51+
4752
module MemberLayout {
4853
header "member-layout.h"
4954
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%{fs-sep}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)