Skip to content

Commit a54fc65

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 a54fc65

File tree

9 files changed

+140
-15
lines changed

9 files changed

+140
-15
lines changed

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: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2801,17 +2801,24 @@ namespace {
28012801
if (!operationFn)
28022802
return RetainReleaseOperationKind::notAfunction;
28032803

2804-
if (operationFn->getParameters()->size() != 1)
2804+
if (operationFn->getParameters()->size() + operationFn->isInstanceMember() != 1)
28052805
return RetainReleaseOperationKind::invalidParameters;
28062806

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-
}
2807+
Type paramType;
2808+
NominalTypeDecl *paramDecl = nullptr;
2809+
if (!operationFn->isInstanceMember()) {
2810+
paramType =
2811+
operationFn->getParameters()->get(0)->getInterfaceType();
2812+
// Unwrap if paramType is an OptionalType
2813+
if (Type optionalType = paramType->getOptionalObjectType()) {
2814+
paramType = optionalType;
2815+
}
28132816

2814-
swift::NominalTypeDecl *paramDecl = paramType->getAnyNominal();
2817+
paramDecl = paramType->getAnyNominal();
2818+
} else {
2819+
paramDecl = cast<NominalTypeDecl>(operationFn->getParent());
2820+
paramType = paramDecl->getDeclaredInterfaceType();
2821+
}
28152822

28162823
// The return type should be void (for release functions), or void
28172824
// or the parameter type (for retain functions).

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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <swift/bridging>
2+
3+
struct RefCountedBox {
4+
int value;
5+
int refCount = 1;
6+
7+
RefCountedBox(int value) : value(value) {}
8+
9+
static RefCountedBox *create(int value) { return new RefCountedBox(value); }
10+
11+
void doRetain() { refCount++; }
12+
void doRelease() { refCount--; }
13+
} SWIFT_SHARED_REFERENCE(.doRetain, .doRelease);
14+
15+
struct DerivedRefCountedBox : RefCountedBox {
16+
int secondValue = 1;
17+
DerivedRefCountedBox(int value, int secondValue)
18+
: RefCountedBox(value), secondValue(secondValue) {}
19+
20+
static DerivedRefCountedBox *createDerived(int value, int secondValue) {
21+
return new DerivedRefCountedBox(value, secondValue);
22+
}
23+
};
24+
25+
// MARK: Retain in a base type, release in derived
26+
27+
struct BaseHasRetain {
28+
mutable int refCount = 1;
29+
void doRetainInBase() const { refCount++; }
30+
};
31+
32+
struct DerivedHasRelease : BaseHasRetain {
33+
int value;
34+
DerivedHasRelease(int value) : value(value) {}
35+
static DerivedHasRelease *create(int value) {
36+
return new DerivedHasRelease(value);
37+
}
38+
39+
void doRelease() const { refCount--; }
40+
} SWIFT_SHARED_REFERENCE(.doRetainInBase, .doRelease);

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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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: }
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -I %swift_src_root/lib/ClangImporter/SwiftBridging -Xfrontend -disable-availability-checking)
2+
3+
// Temporarily disable when running with an older runtime (rdar://128681137)
4+
// UNSUPPORTED: use_os_stdlib
5+
// UNSUPPORTED: back_deployment_runtime
6+
7+
import StdlibUnittest
8+
import LifetimeOperationMethods
9+
10+
var LifetimeMethodsTestSuite = TestSuite("Lifetime operations that are instance methods")
11+
12+
LifetimeMethodsTestSuite.test("retain/release methods") {
13+
let a = RefCountedBox.create(123)!
14+
expectEqual(a.value, 123)
15+
expectTrue(a.refCount > 0)
16+
expectTrue(a.refCount < 10) // optimizations would affect the exact number
17+
}
18+
19+
LifetimeMethodsTestSuite.test("retain/release methods from base type") {
20+
let a = DerivedRefCountedBox.createDerived(321, 456)!
21+
expectEqual(a.value, 321)
22+
expectEqual(a.secondValue, 456)
23+
expectTrue(a.refCount > 0)
24+
expectTrue(a.refCount < 10) // optimizations would affect the exact number
25+
26+
a.secondValue = 789
27+
expectEqual(a.secondValue, 789)
28+
}
29+
30+
LifetimeMethodsTestSuite.test("retain in base type, release in derived type") {
31+
let a = DerivedHasRelease.create(321)!
32+
expectEqual(a.value, 321)
33+
expectTrue(a.refCount > 0)
34+
expectTrue(a.refCount < 10) // optimizations would affect the exact number
35+
}
36+
37+
runAllTests()

0 commit comments

Comments
 (0)