-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[cxx-interop] Allow retain/release operations to be methods #84343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#include <swift/bridging> | ||
|
||
struct RefCountedBox { | ||
int value; | ||
int refCount = 1; | ||
|
||
RefCountedBox(int value) : value(value) {} | ||
|
||
void doRetain() { refCount++; } | ||
void doRelease() { refCount--; } | ||
} SWIFT_SHARED_REFERENCE(.doRetain, .doRelease); | ||
|
||
struct DerivedRefCountedBox : RefCountedBox { | ||
int secondValue = 1; | ||
DerivedRefCountedBox(int value, int secondValue) | ||
: RefCountedBox(value), secondValue(secondValue) {} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think you might be kind enough to also add some templated ref counted base types along the lines of cntrump/MixingLanguagesInAnXcodeProject@main...adetaylor:MixingLanguagesInAnXcodeProject:demonstrate-refcount-probs? Just to make sure this works (and continues to work) for cases like WebKit's? I suspect that the pattern is fairly common since it's a good way to do reference counting without the overhead of virtual functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, let's test that scenario explicitly. |
||
|
||
// MARK: Retain in a base type, release in derived | ||
|
||
struct BaseHasRetain { | ||
mutable int refCount = 1; | ||
void doRetainInBase() const { refCount++; } | ||
}; | ||
|
||
struct DerivedHasRelease : BaseHasRetain { | ||
int value; | ||
DerivedHasRelease(int value) : value(value) {} | ||
|
||
void doRelease() const { refCount--; } | ||
} SWIFT_SHARED_REFERENCE(.doRetainInBase, .doRelease); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens when you have a base class FRT with virtual retain and release methods, and the derived class overrides its retain and release functions? And what about for pure virtual methods? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a great point! In case this does not work as intended and not easy to fix, I believe we could just reject virtual refcounting functions for now and fix these in a follow-up PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That works! We should have a test for it indeed, I just added one. |
||
|
||
// MARK: Retain in a base type, release in templated derived | ||
|
||
template <typename T> | ||
struct TemplatedDerivedHasRelease : BaseHasRetain { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we might want to have a test following the curiously recurring template pattern where the base itself is templated. Similar to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a test for that scenario. |
||
T value; | ||
TemplatedDerivedHasRelease(T value) : value(value) {} | ||
|
||
void doReleaseTemplated() const { refCount--; } | ||
} SWIFT_SHARED_REFERENCE(.doRetainInBase, .doReleaseTemplated); | ||
|
||
using TemplatedDerivedHasReleaseFloat = TemplatedDerivedHasRelease<float>; | ||
using TemplatedDerivedHasReleaseInt = TemplatedDerivedHasRelease<int>; | ||
|
||
// MARK: Retain/release in CRTP base type | ||
|
||
template <typename Derived> | ||
struct CRTPBase { | ||
mutable int refCount = 1; | ||
void crtpRetain() const { refCount++; } | ||
void crtpRelease() const { refCount--; } | ||
} SWIFT_SHARED_REFERENCE(.crtpRetain, .crtpRelease); | ||
|
||
struct CRTPDerived : CRTPBase<CRTPDerived> { | ||
int value; | ||
CRTPDerived(int value) : value(value) {} | ||
}; | ||
|
||
// MARK: Virtual retain and release | ||
|
||
struct VirtualRetainRelease { | ||
int value; | ||
mutable int refCount = 1; | ||
VirtualRetainRelease(int value) : value(value) {} | ||
|
||
virtual void doRetainVirtual() const { refCount++; } | ||
virtual void doReleaseVirtual() const { refCount--; } | ||
virtual ~VirtualRetainRelease() = default; | ||
} SWIFT_SHARED_REFERENCE(.doRetainVirtual, .doReleaseVirtual); | ||
|
||
struct DerivedVirtualRetainRelease : VirtualRetainRelease { | ||
DerivedVirtualRetainRelease(int value) : VirtualRetainRelease(value) {} | ||
|
||
mutable bool calledDerived = false; | ||
void doRetainVirtual() const override { refCount++; calledDerived = true; } | ||
void doReleaseVirtual() const override { refCount--; } | ||
}; | ||
|
||
// MARK: Pure virtual retain and release | ||
|
||
struct PureVirtualRetainRelease { | ||
int value; | ||
mutable int refCount = 1; | ||
PureVirtualRetainRelease(int value) : value(value) {} | ||
|
||
virtual void doRetainPure() const = 0; | ||
virtual void doReleasePure() const = 0; | ||
virtual ~PureVirtualRetainRelease() = default; | ||
} SWIFT_SHARED_REFERENCE(.doRetainPure, .doReleasePure); | ||
|
||
struct DerivedPureVirtualRetainRelease : PureVirtualRetainRelease { | ||
mutable int refCount = 1; | ||
|
||
DerivedPureVirtualRetainRelease(int value) : PureVirtualRetainRelease(value) {} | ||
void doRetainPure() const override { refCount++; } | ||
void doReleasePure() const override { refCount--; } | ||
}; | ||
|
||
// MARK: Static retain/release | ||
#ifdef INCORRECT | ||
struct StaticRetainRelease { | ||
// expected-error@-1 {{specified retain function '.staticRetain' is a static function; expected an instance function}} | ||
// expected-error@-2 {{specified release function '.staticRelease' is a static function; expected an instance function}} | ||
int value; | ||
int refCount = 1; | ||
|
||
StaticRetainRelease(int value) : value(value) {} | ||
|
||
static void staticRetain(StaticRetainRelease* o) { o->refCount++; } | ||
static void staticRelease(StaticRetainRelease* o) { o->refCount--; } | ||
} SWIFT_SHARED_REFERENCE(.staticRetain, .staticRelease); | ||
|
||
struct DerivedStaticRetainRelease : StaticRetainRelease { | ||
// expected-error@-1 {{cannot find retain function '.staticRetain' for reference type 'DerivedStaticRetainRelease'}} | ||
// expected-error@-2 {{cannot find release function '.staticRelease' for reference type 'DerivedStaticRetainRelease'}} | ||
int secondValue = 1; | ||
DerivedStaticRetainRelease(int value, int secondValue) | ||
: StaticRetainRelease(value), secondValue(secondValue) {} | ||
}; | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// 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 | ||
|
||
// CHECK: class RefCountedBox { | ||
// CHECK: func doRetain() | ||
// CHECK: func doRelease() | ||
// CHECK: } | ||
// CHECK: class DerivedRefCountedBox { | ||
// CHECK: func doRetain() | ||
// CHECK: func doRelease() | ||
// CHECK: } | ||
|
||
// CHECK: class DerivedHasRelease { | ||
// CHECK: func doRelease() | ||
// CHECK: func doRetainInBase() | ||
// CHECK: } | ||
|
||
// CHECK: class TemplatedDerivedHasRelease<CFloat> { | ||
// CHECK: var value: Float | ||
// CHECK: func doReleaseTemplated() | ||
// CHECK: func doRetainInBase() | ||
// CHECK: } | ||
// CHECK: class TemplatedDerivedHasRelease<CInt> { | ||
// CHECK: var value: Int32 | ||
// CHECK: func doReleaseTemplated() | ||
// CHECK: func doRetainInBase() | ||
// CHECK: } | ||
|
||
// CHECK: class CRTPDerived { | ||
// CHECK: var value: Int32 | ||
// CHECK: } | ||
|
||
// CHECK: class VirtualRetainRelease { | ||
// CHECK: func doRetainVirtual() | ||
// CHECK: func doReleaseVirtual() | ||
// CHECK: } | ||
// CHECK: class DerivedVirtualRetainRelease { | ||
// CHECK: func doRetainVirtual() | ||
// CHECK: func doReleaseVirtual() | ||
// CHECK: } | ||
|
||
// CHECK: class PureVirtualRetainRelease { | ||
// CHECK: func doRetainPure() | ||
// CHECK: func doReleasePure() | ||
// CHECK: } | ||
// CHECK: class DerivedPureVirtualRetainRelease { | ||
// CHECK: func doRetainPure() | ||
// CHECK: func doReleasePure() | ||
// CHECK: var refCount: Int32 | ||
// CHECK: } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// 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 | ||
|
||
import LifetimeOperationMethods | ||
|
||
let _ = StaticRetainRelease(123) | ||
let _ = DerivedStaticRetainRelease(123, 456) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: you might be able to get rid of this branch via
lookThroughSingleOptionalType
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't actually touch this code in this PR, I'm not sure why GitHub shows it in the diff 😃